1. 概述

本文将讲解如何在Spring Security中获取当前用户信息

Spring中有多种方式可以获取当前登录用户(authenticated principal),让我们先介绍最常见的 —— 手动编码的方式。

2. 从SecurityContextHolder中获取User

最简单方式是通过调用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中获取User

Controller方法中有几种方式可以获取,我们可以Principal作为参数,Spring框架会自动为我们赋值。

@Controller
public class GetUserWithPrincipalController {

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

另外, 我们也可以将Authentication作为参数:

@Controller
public class GetUserWithAuthenticationController {

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

为了尽可能保持灵活性,Authentication接口设计的非常开放。因此, getPrincipal()方法返回的是Object类型,需要我们正确的转换为UserDetails实例。

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

也可直接 从HTTP request中获取:

@Controller
public class GetUserWithHTTPServletRequestController {

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

最后,我们也可以 通过@AuthenticationPrincipal注解方式获取UserDetails实例:

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

4. 通过自定义接口方式获取User

为了充分利用Spring依赖注入,并使我们可以在任何位置获取User(而不只是在@Controller bean中),我们可以自定义一个@Component并结合第一种静态方法方式实现:

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

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

我们自定义的AuthenticationFacade提供了访问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中获取User

同样我们可以在JSP页面中获取当前登录用户,利用spring security taglib支持。首先需要在页面中定义标签:

<%@ 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中获取User

Thymeleaf是一款现代的, 服务端HTML渲染模板引擎, 与Spring MVC具有良好的集成。下面看下如何在Thymeleaf中获取User

首先为了集成ThymeleafSpring Security,我们需要添加thymeleaf-spring5thymeleaf-extras-springsecurity5 依赖:

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

现在我们可以在HTML中使用sec:authorize属性引用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应用中获取当前登录用户信息,开始使用静态方法的方式获取,后面介绍了几种更好的方式,通过注解注入的方式。

惯例,本文完整源代码可从GitHub上获取。这是一个基于Eclipse的项目,所以应该很容易导入和运行。项目运行后,访问主页http://localhost:8080/spring-security-rest-custom/foos/1