小编典典

如何获取活跃用户的 UserDetails

all

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

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

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

例如,类似:

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

但是UsernamePasswordAuthenticationToken我没有得到 ,而是得到了 a UserDetails

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


阅读 116

收藏
2022-08-24

共1个答案

小编典典

序言:@AuthenticationPrincipal自 Spring-Security 3.2
以来,此答案的末尾描述了一个很好的注释。当您使用 Spring-Security >= 3.2 时,这是最好的方法。

当你:

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

然后你可以在你的控制器中使用

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

在配置中你只需要添加这个:

<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。 - 见杰伊的评论


HandlerMethodArgumentResolverSpring 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>

@参见利用 Spring MVC 3.1 HandlerMethodArgumentResolver
接口


Spring-Security 3.2 解决方案

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

它只是在写

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

要使其正常工作,您需要注册AuthenticationPrincipalArgumentResolver(
org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver)
:通过“激活” @EnableWebMvcSecurity或通过在其中注册此 bean mvc:argument-resolvers-
与我在上面的 May Spring 3.1 解决方案中描述的方式相同。

@参见Spring Security 3.2 参考,第 11.2
章。@AuthenticationPrincipal


Spring-Security 4.0 解决方案

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

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

它只是在写

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

要使其正常工作,您需要注册 ( org.springframework.security.web.method.annotation.)
AuthenticationPrincipalArgumentResolver:通过“激活”
@EnableWebMvcSecurity或通过在其中注册此 bean mvc:argument-resolvers- 与我在上面的 May
Spring 3.1 解决方案中描述的方式相同。

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

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

2022-08-24