Spring Security異步無法獲取用戶認(rèn)證信息的解決方法
原因:
Spring Security中的上下文SecurityContext的管理策略有三種
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";
}
默認(rèn)的管理策略是:MODE_THREADLOCAL,對(duì)應(yīng)的策略類是
org.springframework.security.core.context.ThreadLocalSecurityContextHolderStrategy
以下是 Spring Security 初始化策略的代碼:
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";
// 獲取系統(tǒng)配置的策略
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
// 預(yù)初始化策略
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;
}
// Try to load a custom strategy
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
}
ThreadLocalSecurityContextHolderStrategy 里保存Context的方式是 ThreadLocal,ThreadLocal 子線程并不能獲取父類線程的 ThreadLocalMap,
所以就導(dǎo)致了異步無法獲取到用戶認(rèn)證信息。
final class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new ThreadLocal<>();
}
解決方法:
更改上下文的管理策略 SecurityContextHolderStrategy 為 MODE_INHERITABLETHREADLOCAL,即
InheritableThreadLocalSecurityContextHolderStrategy,里面保存上下文的方式是 InheritableThreadLocal,可以獲取父線程的 ThreadLocalMap。
final class InheritableThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<Supplier<SecurityContext>> contextHolder = new InheritableThreadLocal<>();
}
InheritableThreadLocal可以獲取到子線程信息,主要是因?yàn)樗荰hreadLocal的一個(gè)子類,設(shè)計(jì)用來在父子線程間傳遞數(shù)據(jù)。
ThreadLocal變量通常只在當(dāng)前線程中存在,并且每個(gè)線程都有其自己的ThreadLocalMap,
這樣保證了線程間的數(shù)據(jù)隔離。然而,在某些情況下,我們需要父線程中的數(shù)據(jù)能夠在由父線程創(chuàng)建的子線程中被訪問,
這時(shí)就需要使用InheritableThreadLocal。
InheritableThreadLocal的實(shí)現(xiàn)機(jī)制使其能夠在子線程中訪問父線程的數(shù)據(jù)。具體來說,當(dāng)父線程創(chuàng)建一個(gè)子線程時(shí),
InheritableThreadLocal會(huì)采取措施確保子線程可以訪問到父線程中設(shè)置的InheritableThreadLocal變量。
這通常涉及到在子線程的Thread對(duì)象中復(fù)制父線程的ThreadLocalMap,從而使子線程能夠訪問到父線程中設(shè)置的變量。
這種機(jī)制允許在多線程環(huán)境中,特別是在使用線程池等場(chǎng)景下,實(shí)現(xiàn)父子線程間數(shù)據(jù)的傳遞和共享。
需要注意的是,雖然InheritableThreadLocal提供了在子線程中訪問父線程數(shù)據(jù)的能力,但它并不適用于所有情況,
特別是在復(fù)雜的線程交互中可能需要更精細(xì)的控制。此外,使用InheritableThreadLocal時(shí)也需要注意避免內(nèi)存泄漏等問題,因?yàn)槿绻划?dāng)使用,
它可能會(huì)導(dǎo)致資源的不當(dāng)占用。
代碼:
- 1、可以在配置 Security 的配置類中添加 Bean
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
factoryBean.setTargetClass(SecurityContextHolder.class);
factoryBean.setTargetMethod("setStrategyName");
factoryBean.setArguments(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
return factoryBean;
}
}
- 2、可以在配置 Security 的配置類中添加類初始化方式,會(huì)在注入 SecurityContextHolder 之后調(diào)用其中靜態(tài)方法更改 SecurityContextHolderStrategy 的實(shí)現(xiàn)類
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@PostContruct
public void init() {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
}
- 3、可以在啟動(dòng)項(xiàng)加上 -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL 參數(shù),啟動(dòng)時(shí)更改 SecurityContextHolderStrategy 的實(shí)現(xiàn)類
以上就是Spring Security異步無法獲取用戶認(rèn)證信息的解決方法的詳細(xì)內(nèi)容,更多關(guān)于Spring Security異步無法獲取信息的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
在SpringBoot中使用MongoDB的簡(jiǎn)單場(chǎng)景案例
MongoDB 是一種非關(guān)系型數(shù)據(jù)庫(kù),也被稱為 NoSQL 數(shù)據(jù)庫(kù),它主要以文檔的形式存儲(chǔ)數(shù)據(jù),本文給大家介紹了在SpringBoot中使用MongoDB的簡(jiǎn)單場(chǎng)景案例,并通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下2024-09-09
MybatisPlus公共字段自動(dòng)注入方法實(shí)現(xiàn)
本文介紹了MybatisPlus公共字段自動(dòng)注入方法實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-08-08
使用Spring?Boot進(jìn)行單元測(cè)試詳情
這篇文章主要介紹了使用Spring?Boot進(jìn)行單元測(cè)試詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09
MyBatis中實(shí)現(xiàn)數(shù)據(jù)封裝的示例代碼
本文主要介紹了MyBatis中實(shí)現(xiàn)數(shù)據(jù)封裝,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-09-09
java對(duì)同一個(gè)文件進(jìn)行讀寫操作方法
在本篇文章里我們給大家詳細(xì)講述了java對(duì)同一個(gè)文件進(jìn)行讀寫操作的方法和知識(shí)點(diǎn),需要的朋友們可以參考學(xué)習(xí)下。2018-10-10

