Java 如何获得活动用户';用户详细信息

Java 如何获得活动用户';用户详细信息,java,spring,spring-mvc,spring-security,Java,Spring,Spring Mvc,Spring Security,在我的控制器中,当我需要活动(登录)用户时,我将执行以下操作以获取我的UserDetails实现: User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); log.debug(activeUser.getSomeCustomField()); 它工作得很好,但我认为在这种情况下,春天可以让生活变得更轻松。是否有办法将UserDetails自动连接到控制器或方法中

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

User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
它工作得很好,但我认为在这种情况下,春天可以让生活变得更轻松。是否有办法将
UserDetails
自动连接到控制器或方法中

例如,类似于:

public ModelAndView someRequestHandler(Principal principal) { ... }
但是我没有得到
用户名密码身份验证令牌
,而是得到了
用户详细信息


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

Spring安全性旨在与其他非Spring框架协同工作,因此它没有与Spring MVC紧密集成。默认情况下,Spring Security从
HttpServletRequest.getUserPrincipal()
方法返回
Authentication
对象,这就是作为主体得到的。通过使用,您可以直接从中获取
UserDetails
对象

UserDetails ud = ((Authentication)principal).getPrincipal()
还请注意,对象类型可能因所使用的身份验证机制而异(例如,您可能无法获得
UsernamePasswordAuthenticationToken
),并且
身份验证
不必严格包含
UserDetails
。它可以是字符串或任何其他类型

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

interface MySecurityAccessor {

    MyUserDetails getCurrentUser();

    // Other methods
}
然后,您可以通过访问标准实现中的
SecurityContextHolder
来实现这一点,从而将代码与Spring安全性完全分离。然后将其注入需要访问安全信息或当前用户信息的控制器


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

序言:自Spring Security 3.2以来,有一个很好的注释
@AuthenticationPrincipal
。当您使用Spring Security>=3.2时,这是最好的方法

当您:

  • 使用旧版本的Spring Security
  • 需要通过主体中存储的某些信息(如登录名或id)从数据库加载自定义用户对象,或

  • 想了解
    HandlerMethodArgumentResolver
    WebArgumentResolver
    如何优雅地解决这个问题,或者只想了解
    @AuthenticationPrincipal
    AuthenticationPrincipalArgumentResolver
    背后的背景(因为它基于
    HandlerMethodArgumentResolver
然后继续阅读-否则只需使用
@AuthenticationPrincipal
,并感谢Rob Winch(《代码》>@AuthenticationPrincipal的作者)和(感谢他的回答)

(顺便说一句:我的答案有点老(2012年1月),因此它是第一个基于Spring Security 3.2的
@AuthenticationPrincipal
注释解决方案。)


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

public ModelAndView someRequestHandler(Principal principal) {
   User activeUser = (User) ((Authentication) principal).getPrincipal();
   ...
}

如果你需要一次就可以了。但是,如果您需要它好几次,因为它会污染控制器的基础设施细节,这通常应该被框架隐藏起来

因此,您可能真正想要的是有一个这样的控制器:

public ModelAndView someRequestHandler(@ActiveUser User activeUser) {
   ...
}
因此,您只需要实现一个。它有一个方法

Object resolveArgument(MethodParameter methodParameter,
                   NativeWebRequest webRequest)
                   throws Exception
它获取web请求(第二个参数),如果它认为对方法参数(第一个参数)负责,则必须返回
用户

从Spring3.1开始,有一个新概念叫做。如果您使用Spring 3.1+,那么您应该使用它。(答案的下一节将对此进行描述)

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

@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>
在配置中,您需要添加以下内容

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

要使其正常工作,您需要注册
AuthenticationPrincipalArgumentResolver
org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver
):通过“激活”
@EnableWebMvcSecurity
或通过在
mvc:argument resolvers
中注册此bean-与我在上述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 4.0解决方案
它的工作原理与Spring 3.2解决方案类似,但在Spring 4.0中,
@AuthenticationPrincipal
AuthenticationPrincipalArgumentResolver
被“移动”到另一个包中:

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

这只是写作

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

要使其正常工作,您需要注册(
org.springframework.security.web.method.annotation.
AuthenticationPrincipalArgumentResolver
:通过“激活”
@EnableWebMvcSecurity
或通过在
mvc:argument resolvers
中注册此bean-与我在上述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>


@请参见

实现
HandlerInterceptor
接口,然后将
UserDetails
注入到具有模型的每个请求中,如下所示:

@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());
        }
}
虽然提供了一个优雅的解决方案,但使用SpringSecurity3.2,您不再需要实现自己的
ArgumentResolver

如果您有一个
UserDetails
实现
CustomUser
,您可以执行以下操作:

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

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

请参见

从Spring Security 3.2版开始,一些旧版本的答案已经实现了自定义功能
@Controller
public abstract class AbstractController {
    @ModelAttribute("loggedUser")
    public User getLoggedUser() {
        return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
}
@RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(@AuthenticationPrincipal CustomUser customUser) {

    // .. find messages for this User and return them...
}
@Controller
public class MyController {
   @RequestMapping("/user/current/show")
   public String show(@AuthenticationPrincipal CustomUser customUser) {
        // do something with CustomUser
       return "view";
   }
}
<%@ 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>
@GetMapping(value = "/mappingEndPoint") <ReturnType> methodName(Authentication auth) {
   String userName = auth.getName(); 
   return <ReturnType>;
}