在 Spring Security 中获取当前用户

1、概览

本文介绍了如何在 Spring Security 中检索当前用户详细信息。

2、从 Bean 中获取用户

检索当前已通过身份认证用户(Principal)的最简单方式是调用 SecurityContextHolder 的静态方法:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String currentPrincipalName = authentication.getName();

该代码需要改进的地方在于,在尝试访问之前,首先要检查是否存在已通过身份认证的用户:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!(authentication instanceof AnonymousAuthenticationToken)) {
    String currentUserName = authentication.getName();
    return currentUserName;
}

当然,这样的静态调用也有缺点,代码的可测试性降低了。

3、在 Controller 中获取用户

@Controller 中,可以直接将 Principal 定义为方法参数,这样框架就能正确地解析:

@Controller
public class GetUserWithPrincipalController {

    @RequestMapping(value = "/username", method = RequestMethod.GET)
    @ResponseBody
    public String currentUserName(Principal principal) {
        return principal.getName();
    }
}

或者,也可以使用 Authentication Token:

@Controller
public class GetUserWithAuthenticationController {

    @RequestMapping(value = "/username", method = RequestMethod.GET)
    @ResponseBody
    public String currentUserName(Authentication authentication) {
        return authentication.getName();
    }
}

Authentication 类的 API 非常开放,以使框架尽可能灵活。因此,Spring Security 的 Principal 只能作为一个 Object 进行检索,并且需要将其转换为正确的 UserDetails 实例:

UserDetails userDetails = (UserDetails) authentication.getPrincipal();
System.out.println("User has authorities: " + userDetails.getAuthorities());

另外,下面是直接从 HttpServletRequest 中获取用户的代码:

@Controller
public class GetUserWithHTTPServletRequestController {

    @RequestMapping(value = "/username", method = RequestMethod.GET)
    @ResponseBody
    public String currentUserNameSimple(HttpServletRequest request) {
        Principal principal = request.getUserPrincipal();
        return principal.getName();
    }
}

最后,可以使用 @AuthenticationPrincipal 注解来获取当前已认证用户的详细信息:

@RestController
public class GetUserWithAuthenticationPrincipalAnnotationController {
    
    @GetMapping("/user")
    public String getUser(@AuthenticationPrincipal UserDetails userDetails) {
        return "User Details: " + userDetails.getUsername();
    }
}

如上,@AuthenticationPrincipal 注解将当前通过身份认证的用户的 UserDetails 注入到方法中。此注解有助于将 Authentication.getPrincipal() 解析为方法参数。

当用 @AuthenticationPrincipal 对方法参数进行注解时,Spring Security 会自动提供当前已认证用户的 PrincipalPrincipal 代表用户的身份,可以是用户名、用户对象或任何形式的用户标识。

4、通过自定义接口获取用户

为了充分利用 Spring 依赖注入的功能,并能够在任何地方获取认证信息(不仅仅是在 @Controller Bean 中),需要将静态访问封装在一个简单的 Facade(外观设计模式)后面:

public interface IAuthenticationFacade {
    Authentication getAuthentication();
}
@Component
public class AuthenticationFacade implements IAuthenticationFacade {

    @Override
    public Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }
}

这个外观模式暴露了 Authentication 对象,同时隐藏了静态状态,使代码解耦且完全可测试:

@Controller
public class GetUserWithCustomInterfaceController {
    @Autowired
    private IAuthenticationFacade authenticationFacade;

    @RequestMapping(value = "/username", method = RequestMethod.GET)
    @ResponseBody
    public String currentUserNameSimple() {
        Authentication authentication = authenticationFacade.getAuthentication();
        return authentication.getName();
    }
}

5、在 JSP 中获取用户

通过 Spring Security Taglib,还可在 JSP 页面中访问当前已认证的 Principal。

首先,需要在页面中定义标签:

<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>

接下来,可以访问 Principal

<security:authorize access="isAuthenticated()">
    authenticated as <security:authentication property="principal.username" /> 
</security:authorize>

6、在 Thymeleaf 中获取用户

Thymeleaf 是一个现代的服务器端网页模板引擎,与 Spring MVC 框架可以很好地集成。

来看看如何在使用 Thymeleaf 引擎的页面中访问当前已认证的 Principal

首先,需要添加 thymeleaf-spring5thymeleaf-extras-springsecurity5 依赖,以整合 Thymeleaf 与 Spring Security:

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
</dependency>

现在,可以使用 sec:authorize 属性在 HTML 页面中引用 Principal

<html xmlns:th="https://www.thymeleaf.org" 
  xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<body>
    <div sec:authorize="isAuthenticated()">
      Authenticated as <span sec:authentication="name"></span></div>
</body>
</html>

7、总结

本文介绍了如何在 Spring Security 应用中获取当前用户信息,包括从 SecurityContextHolder 获取、使用 @AuthenticationPrincipal 注解注入,以及在 JSP 和 Thymeleaf 中获取。


Ref:https://www.baeldung.com/get-user-in-spring-security