1. 概述
本教程將演示如何使用 Spring Security 檢索用户詳細信息
當前已認證的用户可通過 Spring 中的多種機制訪問。我們首先介紹最常見的解決方案——編程訪問。
2. 將用户放入 Bean
獲取當前認證主體的最簡單方法是使用 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. 在控制器中獲取用户
我們有額外的選項,位於一個@RestController註解的Bean中。
我們可以直接將principal定義為方法參數,框架將正確地解析它:
@RestController
public class GetUserWithPrincipalController {
@GetMapping(value = "/username")
public String currentUserName(Principal principal) {
return principal.getName();
}
}
或者,我們也可以使用身份驗證令牌:
@RestController
public class GetUserWithAuthenticationController {
@GetMapping(value = "/username")
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());
此外,直接從HTTP請求:
@RestController
public class GetUserWithHTTPServletRequestController {
@GetMapping(value = "/username")
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會自動提供當前已認證用户principal。principal代表用户的身份,可以是用户名、用户對象或任何形式的用户標識。
4. 通過自定義接口獲取用户
為了充分利用 Spring 的依賴注入,並能夠在任何地方(不僅僅是 @RestController beans 中)獲取認證,我們需要隱藏靜態訪問背後的簡單外觀模式:public interface IAuthenticationFacade {
Authentication getAuthentication();
}
@Component
public class AuthenticationFacade implements IAuthenticationFacade {
@Override
public Authentication getAuthentication() {
return SecurityContextHolder.getContext().getAuthentication();
}
}
外觀模式暴露了 Authentication 對象,同時隱藏了靜態狀態,並保持代碼解耦和完全可測試:
@RestController
public class GetUserWithCustomInterfaceController {
@Autowired
private IAuthenticationFacade authenticationFacade;
@GetMapping(value = "/username")
public String currentUserNameSimple() {
Authentication authentication = authenticationFacade.getAuthentication();
return authentication.getName();
}
}
5. 在 JSP 中獲取用户
我們可以通過利用 Spring Security Taglib 支持,從 JSP 頁面訪問當前已認證的主體。
首先,我們需要在頁面上定義標籤:
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
接下來,我們可以引用主體:
<security:authorize access="isAuthenticated()">
authenticated as <security:authentication property="principal.username" />
</security:authorize>
6. 在 Thymeleaf 中獲取用户
Thymeleaf 是一種現代的服務器端 Web 模板引擎,與 Spring MVC 框架具有良好的集成性。
讓我們看看如何在 Thymeleaf 引擎的頁面中訪問當前認證的 principal。
首先,我們需要添加 thymeleaf-spring6 和 thymeleaf-extras-springsecurity6 依賴項,以將 Thymeleaf 與 Spring Security 集成:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</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 應用中獲取用户信息的步驟,從常見的靜態訪問機制開始,然後介紹了幾種更好的注入 principal 的方法。
http://localhost:8080/spring-security-rest-custom/foos/1