Spring Security獲取用戶認證信息的實現流程
登錄用戶數據獲取
SecurityContextHolder
? Spring Security 會將登錄用戶數據保存在 Session 中。但是,為了使用方便,Spring Security在此基礎上還做了一些改進,其中最主要的一個變化就是線程綁定。當用戶登錄成功后,Spring Security 會將登錄成功的用戶信息保存到 SecurityContextHolder 中。
? SecurityContextHolder 中的數據保存默認是通過ThreadLocal 來實現的,使用 ThreadLocal 創(chuàng)建的變量只能被當前線程訪問,不能被其他線程訪問和修改,也就是用戶數據和請求線程綁定在一起。當登錄請求處理完畢后,Spring Security 會將 SecurityContextHolder 中的數據拿出來保存到 Session 中,同時將 SecurityContexHolder 中的數據清空。以后每當有請求到來時,Spring Security 就會先從 Session 中取出用戶登錄數據,保存到SecurityContextHolder 中,方便在該請求的后續(xù)處理過程中使用,同時在請求結束時將 SecurityContextHolder 中的數據拿出來保存到 Session 中,然后將SecurityContextHolder 中的數據清空。
? 實際上 SecurityContextHolder 中存儲是 SecurityContext,在 SecurityContext 中存儲是 Authentication。

這種設計是典型的策略設計模式:
public class SecurityContextHolder {
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
private static final String MODE_PRE_INITIALIZED = "MODE_PRE_INITIALIZED";
private static SecurityContextHolderStrategy strategy;
//....
private static void initializeStrategy() {
if (MODE_PRE_INITIALIZED.equals(strategyName)) {
Assert.state(strategy != null, "When using " + MODE_PRE_INITIALIZED
+ ", setContextHolderStrategy must be called with the fully constructed strategy");
return;
}
if (!StringUtils.hasText(strategyName)) {
// Set default
strategyName = MODE_THREADLOCAL;
}
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
return;
}
if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
return;
}
//.....
}
}
MODE THREADLOCAL:這種存放策略是將 SecurityContext 存放在 ThreadLocal中,大家知道 Threadlocal 的特點是在哪個線程中存儲就要在哪個線程中讀取,這其實非常適合 web 應用,因為在默認情況下,一個請求無論經過多少 Filter 到達 Servlet,都是由一個線程來處理的。這也是 SecurityContextHolder 的默認存儲策略,這種存儲策略意味著如果在具體的業(yè)務處理代碼中,開啟了子線程,在子線程中去獲取登錄用戶數據,就會獲取不到。MODE INHERITABLETHREADLOCAL:這種存儲模式適用于多線程環(huán)境,如果希望在子線程中也能夠獲取到登錄用戶數據,那么可以使用這種存儲模式。MODE GLOBAL:這種存儲模式實際上是將數據保存在一個靜態(tài)變量中,在 JavaWeb開發(fā)中,這種模式很少使用到。
SecurityContextHolderStrategy
通過 SecurityContextHolder 可以得知,SecurityContextHolderStrategy 接口用來定義存儲策略方法
public interface SecurityContextHolderStrategy {
void clearContext();
SecurityContext getContext();
void setContext(SecurityContext context);
SecurityContext createEmptyContext();
}
接口中一共定義了四個方法:
clearContext:該方法用來清除存儲的 SecurityContext對象。getContext:該方法用來獲取存儲的 SecurityContext 對象。setContext:該方法用來設置存儲的 SecurityContext 對象。create Empty Context:該方法則用來創(chuàng)建一個空的 SecurityContext 對象。
代碼中獲取認證之后用戶數據
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
Authentication authentication = SecurityContextHolder
.getContext().getAuthentication();
User principal = (User) authentication.getPrincipal();
System.out.println("身份 :"+principal.getUsername());
System.out.println("憑證 :"+authentication.getCredentials());
System.out.println("權限 :"+authentication.getAuthorities());
return "hello security";
}
}
多線程情況下獲取用戶數據
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
new Thread(()->{
Authentication authentication = SecurityContextHolder
.getContext().getAuthentication();
User principal = (User) authentication.getPrincipal();
System.out.println("身份 :"+principal.getUsername());
System.out.println("憑證 :"+authentication.getCredentials());
System.out.println("權限 :"+authentication.getAuthorities());
}).start();
return "hello security";
}
}

可以看到默認策略,是無法在子線程中獲取用戶信息,如果需要在子線程中獲取必須使用第二種策略,默認策略是通過 System.getProperty 加載的,因此我們可以通過增加 VM Options 參數進行修改。
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

頁面上獲取用戶信息
引入依賴
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency>
頁面加入命名空間
<html lang="en" xmlns:th="https://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
頁面中使用
<!--獲取認證用戶名--> <ul> <li sec:authentication="principal.username"></li> <li sec:authentication="principal.authorities"></li> <li sec:authentication="principal.accountNonExpired"></li> <li sec:authentication="principal.accountNonLocked"></li> <li sec:authentication="principal.credentialsNonExpired"></li> </ul>

到此這篇關于Spring Security獲取用戶認證信息的實現流程的文章就介紹到這了,更多相關Spring Security獲取認證信息內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Spring Security整合KeyCloak保護Rest API實現詳解
- Spring?Security中如何獲取AuthenticationManager對象
- SpringSecurity報錯authenticationManager must be spec的解決
- Spring?Security?登錄時添加圖形驗證碼實現實例
- SpringBoot?Security權限控制自定義failureHandler實例
- SpringBoot整合Security權限控制登錄首頁
- SpringBoot?整合Security權限控制的初步配置
- SpringBoot?Security使用MySQL實現驗證與權限管理
相關文章
初識Spring Boot框架之Spring Boot的自動配置
本篇文章主要介紹了初識Spring Boot框架之Spring Boot的自動配置,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04
springboot springmvc拋出全局異常的解決方法
這篇文章主要為大家詳細介紹了springboot springmvc拋出全局異常的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
二種jar包制作方法講解(dos打包jar eclipse打包jar文件)
這篇文章主要介紹了二種jar包制作方法講解:dos打包jar和eclipse打包jar文件,大家參考使用吧2013-11-11
利用Spring Social輕松搞定微信授權登錄的方法示例
這篇文章主要介紹了利用Spring Social輕松搞定微信授權登錄的方法示例,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12

