繁体   English   中英

Spring Boot:将请求范围的,延迟初始化的自定义User对象注入控制器

[英]Spring boot: inject a request-scoped, lazy-initialized custom User object into a controller

我正在构建一个Spring Boot应用程序来提供无状态REST API。 为了安全起见,我们使用OAuth2。我的应用程序收到仅承载令牌。

用户信息存储在我们的数据库中。 我可以在控制器中使用注入的Principal查找它:

@RequestMapping(...)
public void endpoint(Principal p) {
  MyUser user = this.myUserRepository.findById(p.getName());
  ...
}

为了避免增加额外的内容,我希望能够将MyUser对象直接注入到我的控制器方法中。 我该如何实现? (到目前为止,我想出的最好的办法是创建一个懒惰的,请求范围的@Bean ...但是我无法使其正常工作...)

惯用方式

Spring Security中的惯用方式是使用UserDetailsService实现自己的

public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    MyUserRepository myUserRepository;

    public UserDetails loadUserByUsername(String username) {
        return this.myUserRepository.findById(username);
    }
}

然后,根据您的需求,在Spring Security DSL中可以存放多个位置。

与您正在使用的身份验证方法集成(在本例中为OAuth 2.0)之后,您将能够执行以下操作:

public void endpoint(@AuthenticationPrincipal MyUser myuser) {

}

快速但不太灵活的方式

通常最好在身份验证时(确定Principal时)执行此操作,而不是在方法解析时(使用参数解析器)执行此操作,因为这样可以在更多身份验证方案中使用它。

也就是说,您也可以将@AuthenticationPrincipal参数解析器与已注册的任何bean一起使用,例如

public void endpoint(
    @AuthenticationPrincipal(expression="@myBean.convert(#this)") MyUser user) 
{

}

...

@Bean
public Converter<Principal, MyUser> myBean() {
    return principal -> this.myUserRepository.findById(p.getName())
}

折衷是每次调用此方法时都将执行此转换。 由于您的应用程序是无状态的,因此这可能不是问题(因为无论如何都需要对每个请求执行查找),但这意味着该控制器可能无法在其他应用程序配置文件中重用。

您可以通过实现HandlerMethodArgumentResolver来实现。 例如:

自定义注释:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Version {
}

实现方式:

public class HeaderVersionArgumentResolver implements HandlerMethodArgumentResolver {

@Override
public boolean supportsParameter(MethodParameter methodParameter) {
    return methodParameter.getParameterAnnotation(Version.class) != null;
}

@Override
public Object resolveArgument(
  MethodParameter methodParameter, 
  ModelAndViewContainer modelAndViewContainer, 
  NativeWebRequest nativeWebRequest, 
  WebDataBinderFactory webDataBinderFactory) throws Exception {

    HttpServletRequest request 
      = (HttpServletRequest) nativeWebRequest.getNativeRequest();

    return request.getHeader("Version");
}
}

实现此功能时,应将其添加为参数解析器:

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addArgumentResolvers(
  List<HandlerMethodArgumentResolver> argumentResolvers) {
    argumentResolvers.add(new HeaderVersionArgumentResolver());
}
}

现在我们可以将其用作参数

public ResponseEntity findByVersion(@PathVariable Long id, @Version String version) 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM