如何获取活跃用户的用户详细信息

2022-08-31 06:53:41

在我的控制器中,当我需要活动(登录)用户时,我正在执行以下操作来获取我的实现:UserDetails

User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());

它工作得很好,但我认为在这种情况下,春天可以让生活更轻松。有没有办法让自动连接到控制器或方法中?UserDetails

例如,类似下面的内容:

public ModelAndView someRequestHandler(Principal principal) { ... }

但是,我没有得到,我得到的不是?UsernamePasswordAuthenticationTokenUserDetails

我正在寻找一个优雅的解决方案。有什么想法吗?


答案 1

序言:从Spring-Security 3.2开始,在这个答案的末尾描述了一个很好的注释。这是使用Spring-Security >= 3.2时的最佳方法。@AuthenticationPrincipal

当您:

  • 使用旧版本的Spring-Security,
  • 需要通过存储在主体中的一些信息(如登录名或 id)从数据库中加载自定义用户对象,或者
  • 想学习如何以一种优雅的方式解决这个问题,或者只是想学习一个背后的背景和(因为它是基于一个HandlerMethodArgumentResolverWebArgumentResolver@AuthenticationPrincipalAuthenticationPrincipalArgumentResolverHandlerMethodArgumentResolver)

然后继续阅读 - 否则只需使用并感谢Rob Winch(作者)和Lukas Schmelzeisen(他的答案)。@AuthenticationPrincipal@AuthenticationPrincipal

(顺便说一句:我的答案有点旧(2012年1月),所以Lukas Schmelzeisen是第一个基于Spring Security 3.2的@AuthenticationPrincipal注释解决方案。


然后,您可以在控制器中使用

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 请求(第二个参数),并且必须返回 if 它感觉对方法参数(第一个参数)负责。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;
        }
   }
}

您需要定义自定义注释 -- 如果 User 的每个实例都应始终从安全上下文中获取,但绝不是命令对象,则可以跳过它。

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

在配置中,您只需要添加以下内容:

<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,他们建议使用HandlerMethodArgumentResolver而不是WebArgumentResolver。- 见杰伊的评论


HererMethodArgumentResolver for Spring 3.1 +相同

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

在配置中,您需要添加此

<mvc:annotation-driven>
      <mvc:argument-resolvers>
           <bean class="CurrentUserHandlerMethodArgumentResolver"/>         
      </mvc:argument-resolvers>
 </mvc:annotation-driven>

@See 利用 Spring MVC 3.1 HandlerMethodArgumentResolver 接口


弹簧-安全3.2解决方案

Spring Security 3.2(不要与Spring 3.2混淆)有自己的内置解决方案:@AuthenticationPrincipal()。这在Lukas Schmelzeisen的回答中得到了很好的描述。org.springframework.security.web.bind.annotation.AuthenticationPrincipal

它只是写作

ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
    ...
 }

要使这项工作正常工作,您需要注册():通过“激活”或通过注册此bean - 就像我在上面的may Spring 3.1解决方案中描述的那样。AuthenticationPrincipalArgumentResolverorg.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver@EnableWebMvcSecuritymvc:argument-resolvers

@See Spring Security 3.2 参考,第 11.2 章,@AuthenticationPrincipal


弹簧-安防4.0解决方案

它的工作方式类似于Spring 3.2解决方案,但在Spring 4.0中,和被“移动”到另一个包中:@AuthenticationPrincipalAuthenticationPrincipalArgumentResolver

(但是旧包装中的旧类仍然存在,所以不要混合它们!

它只是写作

import org.springframework.security.core.annotation.AuthenticationPrincipal;
ModelAndView someRequestHandler(@AuthenticationPrincipal User activeUser) {
    ...
}

要使这项工作正常工作,您需要注册():通过“激活”或通过注册此bean - 就像我在上面的may Spring 3.1解决方案中描述的那样。org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver@EnableWebMvcSecuritymvc:argument-resolvers

<mvc:annotation-driven>
    <mvc:argument-resolvers>
        <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
    </mvc:argument-resolvers>
</mvc:annotation-driven>

@See Spring Security 5.0 参考,第 39.3 章 @AuthenticationPrincipal


答案 2

虽然Ralphs Answer提供了一个优雅的解决方案,但有了Spring Security 3.2,您不再需要实现自己的.ArgumentResolver

如果你有一个实现,你可以这样做:UserDetailsCustomUser

@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {

    // .. find messages for this User and return them...
}

请参阅Spring安全文档:@AuthenticationPrincipal


推荐