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,對應(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會采取措施確保子線程可以訪問到父線程中設(shè)置的InheritableThreadLocal變量。
這通常涉及到在子線程的Thread對象中復(fù)制父線程的ThreadLocalMap,從而使子線程能夠訪問到父線程中設(shè)置的變量。
這種機(jī)制允許在多線程環(huán)境中,特別是在使用線程池等場景下,實(shí)現(xiàn)父子線程間數(shù)據(jù)的傳遞和共享。
需要注意的是,雖然InheritableThreadLocal提供了在子線程中訪問父線程數(shù)據(jù)的能力,但它并不適用于所有情況,
特別是在復(fù)雜的線程交互中可能需要更精細(xì)的控制。此外,使用InheritableThreadLocal時(shí)也需要注意避免內(nèi)存泄漏等問題,因?yàn)槿绻划?dāng)使用,
它可能會導(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 的配置類中添加類初始化方式,會在注入 SecurityContextHolder 之后調(diào)用其中靜態(tài)方法更改 SecurityContextHolderStrategy 的實(shí)現(xiàn)類
@Configuration @EnableWebSecurity public class SecurityConfig { @PostContruct public void init() { SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); } }
- 3、可以在啟動項(xiàng)加上 -Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL 參數(shù),啟動時(shí)更改 SecurityContextHolderStrategy 的實(shí)現(xiàn)類
以上就是Spring Security異步無法獲取用戶認(rèn)證信息的解決方法的詳細(xì)內(nèi)容,更多關(guān)于Spring Security異步無法獲取信息的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring Boot 和 Spring 到底有啥區(qū)別你知道嗎
Spring Boot框架的核心就是自動配置,只要存在相應(yīng)的jar包,Spring就幫我們自動配置。接下來通過本文給大家介紹Spring與Spring boot的區(qū)別介紹,非常不錯(cuò),需要的朋友參考下吧2021-08-08uploadify上傳及后臺文件合法性驗(yàn)證的代碼解析
這篇文章主要介紹了uploadify上傳及后臺文件合法性驗(yàn)證的代碼解析,整段代碼分為后臺上傳方法,文件合法性驗(yàn)證類,前端上傳js,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Javaweb開發(fā)中通過Servlet生成驗(yàn)證碼圖片
這篇文章主要為大家詳細(xì)介紹了Javaweb開發(fā)中通過Servlet生成驗(yàn)證碼圖片的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05java map中相同的key保存多個(gè)value值方式
這篇文章主要介紹了java map中相同的key保存多個(gè)value值方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08