簡體   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