通过Spring Security向控制器注入自定义主体

通过Spring Security向控制器注入自定义主体,spring,spring-security,Spring,Spring Security,servletApi()对Spring安全性的支持非常好 我希望注入自定义主体,如下所示: public interface UserPrincipal extends Principal { public Integer getId(); } @RequestMapping(value = "/") public ResponseEntity<List<Conversation>> listAfter(UserPrincipal user){ // imp

servletApi()对Spring安全性的支持非常好

我希望注入自定义主体,如下所示:

public interface UserPrincipal extends Principal {
   public Integer getId();
}

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipal user){
   // implementation
}  

or


@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipalImpl user){
   // implementation
}
else if (Principal.class.isAssignableFrom(paramType)) {
    return request.getUserPrincipal();
}
这是问题开始的地方<代码>请求是
SecurityContextHolderAwareRequestWrapper
的一个实例。它实现了以下功能:

@Override
public Principal getUserPrincipal() {
    Authentication auth = getAuthentication();

    if ((auth == null) || (auth.getPrincipal() == null)) {
        return null;
    }

    return auth;
 }
因为
身份验证
也是
主体
。(到目前为止,我对spring security唯一不喜欢的部分。我也会单独问这个问题。)

这导致了一个问题。因为
身份验证
主体
而不是
用户主体

我如何解决这个问题?我是否也需要实现一个用户主体身份验证?或者我应该更改HandlerMethodArgumentResolver顺序以创建自定义解析器?(这对于SpringMVC来说并不容易,因为内部处理程序具有更高的优先级。)

作为额外信息:

我使用的是Spring Security M2,我对AuthenticationManagerBuilder的配置很简单:

@Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception        {

  auth
     .userDetailsService(detailsService);
}

有什么帮助吗?

从根本上说,这似乎是与Spring MVC集成的问题,而不是Spring的安全问题。Spring Security无法知道这一点Authentication@getPrinicpal()实现主体,因为API返回一个对象

我给你看了一些选择。每种方法都有一些优点和缺点,但我认为最好的方法是使用
@modeldattribute
@ControllerAdvice

@modeldattribute
@ControllerAdvice
最简单的选项是在自定义
@ControllerAdvice
上使用
@modeldattribute
注释方法。您可以在中找到详细信息

现在,在控制器中,您可以执行以下操作:

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(@ModelAttribute UserPrincipal user){
   // implementation
}
@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(
  @Value("#{request.userPrincipal.principal}") UserPrincipal user){
   // implementation
}
这是因为HttpServletRequest作为ExpressionValueMethodArgumentResolver(由SpringMVC默认添加)的一个属性可用,该属性允许通过访问内容。我发现它不如
@modeldattribute
吸引人,因为
@Value
注释中必须有常量。解决后会更好,这将允许使用您自己的自定义注释,如:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Value("#{request.userPrincipal.principal}")
public @interface CurrentUser { }
@Autowire RequestMappingHandlerAdapter 这有点草率,因为RequestMappingHandlerAdapter已经初始化,但是您可以更改HandlerMethodArgumentResolver的顺序,如下所示:

@EnableWebMvc
@Configuration
public class WebMvcConfiguration 
  extends WebMvcConfigurerAdapter {
    ...
    @Autowired
    public void setArgumentResolvers(RequestMappingHandlerAdapter adapter) {
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
        resolvers.add(new CustomPrincipalArgumentResolver());
        resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
        adapter.setArgumentResolvers(resolvers);
    }
}

我知道这是一个老生常谈的问题,但在谷歌搜索校长时,它确实会出现在首位,我将发布2020年的更新:

自Spring Security 4.0以来,您只需将
@AuthenticationPrincipal
注入控制器方法即可:

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(@AuthenticationPrincipal UserPrincipal user){
   // implementation
} 
@RequestMapping(value=“/”)
public ResponseEntity listAfter(@AuthenticationPrincipal UserPrincipal user){
//实施
} 

这将是现成的,不需要额外的配置。

这对您有帮助吗:--是一个稍微不同的问题,但我认为最终您会尝试解决相同的问题。谢谢@Ralph,但它无法帮助我。我的情况和那个问题没什么不同。有很大的区别,也不喜欢显式强制转换身份验证,这意味着我的模块也必须依赖于Spring安全性。Rob,非常感谢。你真的教会了我很多东西今天我已经实现了一个自定义方法参数处理程序。为了改变处理程序的顺序,我不得不使用反射:)您的实现比我的更好。谢谢你的回答。解释得很好。谢谢,说得好。顺便说一句,是@Rob Winch实现了
@AuthenticationPrincipal
/
AuthenticationPrincipalArgumentResolver
:)
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
    ...

    @Bean
    @Override
    public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
        RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter()();
        List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
        resolvers.add(new CustomPrincipalArgumentResolver());
        resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
        adapter.setArgumentResolvers(resolvers);
        return adapter;
    }
}
@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(@AuthenticationPrincipal UserPrincipal user){
   // implementation
}