为了账号安全,请及时绑定邮箱和手机立即绑定

如何获取活动用户的UserDetails

/ 猿问

如何获取活动用户的UserDetails

慕仰0522570 2019-07-25 15:54:45

如何获取活动用户的UserDetails

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

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

它工作正常,但我认为Spring可以在这样的情况下让生活更轻松。有没有办法将UserDetails自动装配到控制器或方法?

例如,类似于:

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

但是,而不是得到UsernamePasswordAuthenticationToken,我得到了一个UserDetails

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


查看完整描述

3 回答

?
12345678_0001

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

当你:

  • 使用旧版本的Spring-Security,

  • 需要通过存储在主体中的一些信息(如登录名或ID)从数据库加载自定义用户对象

  • 想了解如何HandlerMethodArgumentResolverWebArgumentResolver在一个优雅的方式解决这个问题,或者只是想一个学习后面的背景@AuthenticationPrincipalAuthenticationPrincipalArgumentResolver(因为它是基于一个HandlerMethodArgumentResolver

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

(顺便说一句:我的答案有点老了(2012年1月),所以Lukas Schmelzeisen是第一个带有@AuthenticationPrincipalSpring Security 3.2注释解决方案的人。)


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

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

从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)@Documentedpublic @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,他们会在WebArgumentResolver上推荐HandlerMethodArgumentResolver。 - 见Jay的评论


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>

@See 利用Spring MVC 3.1 HandlerMethodArgumentResolver接口


Spring-Security 3.2解决方案

春季安全3.2(不使用Spring 3.2混淆)有自己打造的解决方案:@AuthenticationPrincipalorg.springframework.security.web.bind.annotation.AuthenticationPrincipal)。这在Lukas Schmelzeisen的回答中得到了很好的描述

这只是写作

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

为了实现这一点,你需要注册AuthenticationPrincipalArgumentResolverorg.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver):通过“激活”@EnableWebMvcSecurity或通过注册这个bean mvc:argument-resolvers- 就像我在上面提到的Spring 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- 就像我在上面提到的Spring 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


查看完整回答
反对 回复 2019-07-25
?
茅侃侃

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

如果您有UserDetails实现CustomUser,您可以这样做:

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

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

请参阅Spring Security Documentation:@AuthenticationPrincipal


查看完整回答
反对 回复 2019-07-25
?
呼唤远方

Spring Security旨在与其他非Spring框架一起使用,因此它没有与Spring MVC紧密集成。默认情况下,Spring Security会AuthenticationHttpServletRequest.getUserPrincipal()方法返回对象,因此您将获得作为主体的内容。您可以UserDetails使用以下方法直接从中获取对象

UserDetails ud = ((Authentication)principal).getPrincipal()

另请注意,对象类型可能会有所不同,具体取决于所使用的身份验证机制(UsernamePasswordAuthenticationToken例如,您可能不会获得),并且Authentication严格来说不必包含a UserDetails。它可以是字符串或任何其他类型。

如果您不想SecurityContextHolder直接调用,最优雅的方法(我将遵循)是注入您自己的自定义安全上下文访问器接口,该接口是自定义的,以匹配您的需求和用户对象类型。使用相关方法创建接口,例如:

interface MySecurityAccessor {

    MyUserDetails getCurrentUser();

    // Other methods}

然后,您可以通过访问SecurityContextHolder标准实现来实现此功能,从而完全将您的代码与Spring Security分离。然后将其注入需要访问当前用户的安全信息或信息的控制器。

另一个主要好处是很容易使用固定数据进行简单的实现测试,而不必担心填充线程本地等等。


查看完整回答
反对 回复 2019-07-25

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信