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

這種設(shè)計(jì)是典型的策略設(shè)計(jì)模式:
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 的特點(diǎn)是在哪個(gè)線程中存儲(chǔ)就要在哪個(gè)線程中讀取,這其實(shí)非常適合 web 應(yīng)用,因?yàn)樵谀J(rèn)情況下,一個(gè)請(qǐng)求無論經(jīng)過多少 Filter 到達(dá) Servlet,都是由一個(gè)線程來處理的。這也是 SecurityContextHolder 的默認(rèn)存儲(chǔ)策略,這種存儲(chǔ)策略意味著如果在具體的業(yè)務(wù)處理代碼中,開啟了子線程,在子線程中去獲取登錄用戶數(shù)據(jù),就會(huì)獲取不到。MODE INHERITABLETHREADLOCAL:這種存儲(chǔ)模式適用于多線程環(huán)境,如果希望在子線程中也能夠獲取到登錄用戶數(shù)據(jù),那么可以使用這種存儲(chǔ)模式。MODE GLOBAL:這種存儲(chǔ)模式實(shí)際上是將數(shù)據(jù)保存在一個(gè)靜態(tài)變量中,在 JavaWeb開發(fā)中,這種模式很少使用到。
SecurityContextHolderStrategy
通過 SecurityContextHolder 可以得知,SecurityContextHolderStrategy 接口用來定義存儲(chǔ)策略方法
public interface SecurityContextHolderStrategy {
void clearContext();
SecurityContext getContext();
void setContext(SecurityContext context);
SecurityContext createEmptyContext();
}
接口中一共定義了四個(gè)方法:
clearContext:該方法用來清除存儲(chǔ)的 SecurityContext對(duì)象。getContext:該方法用來獲取存儲(chǔ)的 SecurityContext 對(duì)象。setContext:該方法用來設(shè)置存儲(chǔ)的 SecurityContext 對(duì)象。create Empty Context:該方法則用來創(chuàng)建一個(gè)空的 SecurityContext 對(duì)象。
代碼中獲取認(rèn)證之后用戶數(shù)據(jù)
@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("權(quán)限 :"+authentication.getAuthorities());
return "hello security";
}
}
多線程情況下獲取用戶數(shù)據(jù)
@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("權(quán)限 :"+authentication.getAuthorities());
}).start();
return "hello security";
}
}

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

頁(yè)面上獲取用戶信息
引入依賴
<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity5</artifactId> <version>3.0.4.RELEASE</version> </dependency>
頁(yè)面加入命名空間
<html lang="en" xmlns:th="https://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
頁(yè)面中使用
<!--獲取認(rèn)證用戶名--> <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>

到此這篇關(guān)于Spring Security獲取用戶認(rèn)證信息的實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)Spring Security獲取認(rèn)證信息內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring Security整合KeyCloak保護(hù)Rest API實(shí)現(xiàn)詳解
- Spring?Security中如何獲取AuthenticationManager對(duì)象
- SpringSecurity報(bào)錯(cuò)authenticationManager must be spec的解決
- Spring?Security?登錄時(shí)添加圖形驗(yàn)證碼實(shí)現(xiàn)實(shí)例
- SpringBoot?Security權(quán)限控制自定義failureHandler實(shí)例
- SpringBoot整合Security權(quán)限控制登錄首頁(yè)
- SpringBoot?整合Security權(quán)限控制的初步配置
- SpringBoot?Security使用MySQL實(shí)現(xiàn)驗(yàn)證與權(quán)限管理
相關(guān)文章
初識(shí)Spring Boot框架之Spring Boot的自動(dòng)配置
本篇文章主要介紹了初識(shí)Spring Boot框架之Spring Boot的自動(dòng)配置,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-04-04
SpringBoot實(shí)現(xiàn)重試機(jī)制的四種方案
在分布式系統(tǒng)和微服務(wù)架構(gòu)中,服務(wù)調(diào)用失敗是不可避免的現(xiàn)象,網(wǎng)絡(luò)不穩(wěn)定、服務(wù)過載、臨時(shí)故障等因素都可能導(dǎo)致調(diào)用失敗,重試機(jī)制作為一種處理臨時(shí)性故障的解決方案,能夠有效提高系統(tǒng)的可用性,需要的朋友可以參考下2025-04-04
Java實(shí)現(xiàn)簡(jiǎn)單班級(jí)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單班級(jí)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
springboot springmvc拋出全局異常的解決方法
這篇文章主要為大家詳細(xì)介紹了springboot springmvc拋出全局異常的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06
使用@DS輕松解決動(dòng)態(tài)數(shù)據(jù)源的問題
這篇文章主要介紹了使用@DS輕松解決動(dòng)態(tài)數(shù)據(jù)源的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
利用Java實(shí)現(xiàn)word導(dǎo)入導(dǎo)出富文本(含圖片)的詳細(xì)代碼
這篇文章主要為大家詳細(xì)介紹了利用Java實(shí)現(xiàn)word導(dǎo)入導(dǎo)出富文本(含圖片),文中的示例代碼講解詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以學(xué)習(xí)一下2024-02-02
二種jar包制作方法講解(dos打包jar eclipse打包jar文件)
這篇文章主要介紹了二種jar包制作方法講解:dos打包jar和eclipse打包jar文件,大家參考使用吧2013-11-11
利用Spring Social輕松搞定微信授權(quán)登錄的方法示例
這篇文章主要介紹了利用Spring Social輕松搞定微信授權(quán)登錄的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-12-12

