如何获得活跃用户的UserDetails
在我的控制器中,当我需要活动(login)的用户,我正在做以下来获得我的UserDetails实现: 
 User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); log.debug(activeUser.getSomeCustomField()); 
 它工作正常,但我认为spring可以在这样的情况下使生活更轻松。 有没有办法让UserDetails自动装入控制器或方法? 
例如,像这样的东西:
 public ModelAndView someRequestHandler(Principal principal) { ... } 
 但是,而不是得到UsernamePasswordAuthenticationToken ,我得到一个UserDetails ? 
我正在寻找一个优雅的解决scheme。 有任何想法吗?
  序言:自从Spring-Security 3.2以来,在这个答案的末尾描述了一个很好的注解@AuthenticationPrincipal 。 这是使用Spring-Security> = 3.2时最好的方法。 
当你:
- 使用旧版本的Spring-Security,
 - 需要从数据库加载一个自定义的用户对象的一些信息(如login或ID)存储在主体或
 -  想要了解一个
HandlerMethodArgumentResolver或WebArgumentResolver如何以优雅的方式解决这个问题,或者只是想了解@AuthenticationPrincipal和AuthenticationPrincipalArgumentResolver背后的背景(因为它基于HandlerMethodArgumentResolver) 
 然后继续阅读 – 否则只需使用@AuthenticationPrincipal并感谢Rob Winch( @AuthenticationPrincipal作者)和Lukas Schmelzeisen (他的回答)。 
  (顺便说一句:我的答案有点老(2012年1月),所以Lukas Schmelzeisen是基于Spring Security 3.2的@AuthenticationPrincipal注解解决scheme中的第一个。) 
然后你可以在你的控制器中使用
 public ModelAndView someRequestHandler(Principal principal) { User activeUser = (User) ((Authentication) principal).getPrincipal(); ... } 
没关系,如果你需要一次。 但是,如果你需要它几次丑陋,因为它会污染你的控制器与基础设施的细节,通常应该隐藏的框架。
所以你可能真正想要的是有一个像这样的控制器:
 public ModelAndView someRequestHandler(@ActiveUser User activeUser) { ... } 
 因此你只需要实现一个WebArgumentResolver 。 它有一个方法 
 Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception 
 获取Web请求(第二个参数),并且必须返回User如果它感觉负责方法参数(第一个参数)。 
  自Spring 3.1以来,有一个叫做HandlerMethodArgumentResolver的新概念。  如果你使用Spring 3.1+,那么你应该使用它。  (这个答案在下一节中有描述)) 
 public class CurrentUserWebArgumentResolver implements WebArgumentResolver{ Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) { if(methodParameter is for type User && methodParameter is annotated with @ActiveUser) { Principal principal = webRequest.getUserPrincipal(); return (User) ((Authentication) principal).getPrincipal(); } else { return WebArgumentResolver.UNRESOLVED; } } } 
您需要定义自定义注释 – 如果用户的每个实例始终从安全上下文中取出,但不是命令对象,则可以跳过它。
 @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ActiveUser {} 
在configuration中你只需要添加这个:
 <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" id="applicationConversionService"> <property name="customArgumentResolver"> <bean class="CurrentUserWebArgumentResolver"/> </property> </bean> 
@See: 学习定制Spring MVC @Controller方法参数
应该注意的是,如果你使用Spring 3.1,他们推荐使用WebArgumentResolver的HandlerMethodArgumentResolver。 – 看Jay的评论
  与Spring 3.1+的HandlerMethodArgumentResolver相同 
 public class CurrentUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.getParameterAnnotation(ActiveUser.class) != null && methodParameter.getParameterType().equals(User.class); } @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { if (this.supportsParameter(methodParameter)) { Principal principal = webRequest.getUserPrincipal(); return (User) ((Authentication) principal).getPrincipal(); } else { return WebArgumentResolver.UNRESOLVED; } } } 
在configuration中你需要添加这个
 <mvc:annotation-driven> <mvc:argument-resolvers> <bean class="CurrentUserHandlerMethodArgumentResolver"/> </mvc:argument-resolvers> </mvc:annotation-driven> 
@See 利用Spring MVC 3.1 HandlerMethodArgumentResolver接口
Spring-Security 3.2解决scheme
  Spring Security 3.2(不要和Spring 3.2混淆)有一个自己的解决scheme: @AuthenticationPrincipal 。 这在Lukas Schmelzeisen的答案中有很好的描述 
这只是写作
 ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) { ... } 
 为了得到这个工作,你需要注册AuthenticationPrincipalArgumentResolver :通过“激活” @EnableWebMvcSecurity或者在mvc:argument-resolvers注册这个bean  – 就像我之前用Spring 3.1的解决scheme描述的那样。 
@查看Spring Security 3.2参考,第11.2章。 @AuthenticationPrincipal
 虽然Ralphs Answer提供了一个优雅的解决scheme,但Spring Security 3.2不再需要实现自己的ArgumentResolver 。 
 如果你有一个UserDetails实现CustomUser你可以这样做: 
 @RequestMapping("/messages/inbox") public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) { // .. find messags for this user and return them ... } 
请参阅Spring Security文档:@AuthenticationPrincipal
  Spring Security旨在与其他非Spring框架一起工作,因此它不与Spring MVC紧密集成。  Spring Security默认从HttpServletRequest.getUserPrincipal()方法返回Authentication对象,这就是你得到的主体。 你可以通过使用直接获取你的UserDetails对象 
 UserDetails ud = ((Authentication)principal).getPrincipal() 
 另请注意,对象types可能因使用的身份validation机制而异(例如,您可能不会获得UsernamePasswordAuthenticationToken ),并且Authentication不一定必须包含UserDetails 。 它可以是一个string或任何其他types。 
 如果你不想直接调用SecurityContextHolder ,最优雅的方法(我会遵循)是注入自定义的安全上下文访问器接口,它是自定义的,以满足您的需求和用户对象types。 用相关的方法创build一个接口,例如: 
 interface MySecurityAccessor { MyUserDetails getCurrentUser(); // Other methods } 
 然后,您可以通过访问标准实现中的SecurityContextHolder来实现此目的,从而完全将您的代码与Spring Security解耦。 然后将其注入到需要访问当前用户的安全信息或信息的控制器中。 
另一个主要的好处是可以很简单地使用固定的数据进行testing,而不必担心填充线程本地等等。
 实现HandlerInterceptor接口,然后将UserDetails注入到每个具有Model的请求中,如下所示: 
 @Component public class UserInterceptor implements HandlerInterceptor { ....other methods not shown.... public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { if(modelAndView != null){ modelAndView.addObject("user", (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal()); } } 
 从Spring Security版本3.2开始,由一些旧的答案实现的自定义function,以AuthenticationPrincipalArgumentResolver支持的@AuthenticationPrincipal注释forms@AuthenticationPrincipal即用。 
它的一个简单的例子是:
 @Controller public class MyController { @RequestMapping("/user/current/show") public String show(@AuthenticationPrincipal CustomUser customUser) { // do something with CustomUser return "view"; } } 
  CustomUser需要从authentication.getPrincipal() 
以下是AuthenticationPrincipal和AuthenticationPrincipalArgumentResolver的相应Javadoc
 @Controller public abstract class AbstractController { @ModelAttribute("loggedUser") public User getLoggedUser() { return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); } } 
如果你需要在模板(如JSP)中使用授权用户
 <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <sec:authentication property="principal.yourCustomField"/> 
和…一起
  <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring-security.version}</version> </dependency>