通过Spring Security向控制器注入自定义主体 [英] Injecting Custom Principal to Controllers by Spring Security

查看:171
本文介绍了通过Spring Security向控制器注入自定义主体的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

servletApi()支持非常棒.

servletApi() support of Spring Security is great.

我要这样注入自定义主体:

I want to inject custom Principal as this:

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
}

Spring支持在ServletRequestMethodArgumentResolver的帮助下注入Principal实例.

Spring has support for injecting Principal instances with the help of ServletRequestMethodArgumentResolver.

它是这样注入主体的:

else if (Principal.class.isAssignableFrom(paramType)) {
    return request.getUserPrincipal();
}

这是问题开始的地方. request在这里是SecurityContextHolderAwareRequestWrapper的实例.它具有以下实现:

Here is the place where the problem begins. request is here an instance of SecurityContextHolderAwareRequestWrapper. It has an implementation of:

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

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

    return auth;
 }

因为Authentication也是Principal. (到目前为止,我不喜欢春季安全性的唯一部分.我也将单独询问这个问题.)

Because an Authentication is also an Principal. (The only part of spring security I did not like so far. I will ask this a separate question as well.)

这引起了问题.因为AuthenticationPrincipal而不是UserPrincipal.

This is causing a problem. Because Authentication is a Principal not a UserPrincipal.

如何解决此问题?我是否还需要实现一个UserPrincipal身份验证?还是应该更改HandlerMethodArgumentResolver的顺序来创建自定义解析器? (这对于Spring MVC来说并不容易,因为内部处理程序具有更高的优先级.)

How can I resolve this problem? Do I need to implement an authentication which is a UserPrincipal as well? Or should I change HandlerMethodArgumentResolver order a create a custom resolver? (This is not easy for Spring MVC because internal handlers has higher priority.)

作为附加信息:

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

I am using Spring Security M2 and my configuration for AuthenticationManagerBuilder is simply:

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

  auth
     .userDetailsService(detailsService);
}

有帮助吗?

推荐答案

从根本上讲,这似乎与Spring MVC集成时遇到了麻烦,而不是Spring Security问题.由于API返回对象,Spring Security无法知道Authentication @ getPrinicpal()实现了Principal.

Fundamentally this seems like trouble integrating with Spring MVC and not a Spring Security issue. Spring Security has no way of knowing that Authentication@getPrinicpal() implements Principal since the API returns an Object.

我为您提供了一些选择.每个都有优点和缺点,但我认为最好是使用@ModelAttribute@ControllerAdvice

I see a few options for you. Each has some pros and cons, but I think the best is using @ModelAttribute and @ControllerAdvice

最简单的选项是在自定义@ControllerAdvice上使用@ModelAttribute注释方法.您可以在春季参考书.

The easiest option is annotate a method with @ModelAttribute on custom @ControllerAdvice. You can find details in the Spring Reference.

@ControllerAdvice
public class SecurityControllerAdvice {

    @ModelAttribute
    public UserPrincipal customPrincipal(Authentication a) {
        return (UserPrincipal) a == null ? null : a.getPrincipal();
    }
}

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

Now in your controller you can do something like this:

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

请注意,仅@ModelAttribute是必需的,以确保在HttpServletRequest#getPrincipal()上使用@ModelAttribute.如果未实现Principal,则不需要@ModelAttribute.

Note that the @ModelAttribute is necessary only to ensure the @ModelAttribute is used over the HttpServletRequest#getPrincipal(). If it did not implement Principal, @ModelAttribute is not required.

您还可以执行以下操作:

You can also do something like this:

@RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(
  @Value("#{request.userPrincipal.principal}") UserPrincipal user){
   // implementation
}

之所以可行,是因为HttpServletRequest可以作为ExpressionValueMethodArgumentResolver的属性使用(Spring MVC默认添加),该属性允许通过 SPR-10760 会更好,这将允许您使用自己的自定义注释像这样使用:

This works because the HttpServletRequest is available as an attribute to the ExpressionValueMethodArgumentResolver (added by default by Spring MVC) which allows accessing things via SpEL. I find this less attractive than @ModelAttribute due to the constant that must be in the @Value annotation. It will be nicer when SPR-10760 is resolved which would allow for your own custom annotation to be used like:

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Value("#{request.userPrincipal.principal}")
public @interface CurrentUser { }

@Autowire RequestMappingHandlerAdapter

这有点草率,因为RequestMappingHandlerAdapter已被初始化,但是您可以更改HandlerMethodArgumentResolvers的顺序,如下所示:

@Autowire RequestMappingHandlerAdapter

This is a bit sloppy because the RequestMappingHandlerAdapter has already been initialized, but you can change the ordering of the HandlerMethodArgumentResolvers as shown here:

@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);
    }
}

子类WebMvcConfigurationSupport

您也可以扩展WebMvcConfigurationSupport而不是使用@EnableWebMvc来确保首先使用HandlerMethodArgumentResolver.例如:

Subclass WebMvcConfigurationSupport

You can also extend WebMvcConfigurationSupport instead of using @EnableWebMvc to ensure your HandlerMethodArgumentResolver is used first. For example:

@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;
    }
}

这篇关于通过Spring Security向控制器注入自定义主体的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆