SpringSecurity之SecurityContextHolder使用解讀
之前在使用SpringSecurity的過程中并沒有把很多東西理解透徹,找了很多學(xué)習(xí)資料也都只是是很淺顯了告訴你怎么使用這個(gè)東西?,F(xiàn)在只能自己回過頭來研究研究。。。。終究是一個(gè)人扛下了所有/(ㄒoㄒ)/~~。
GO,現(xiàn)在越看spring源碼越覺得里面注釋是真的詳細(xì),雖然英語很菜耐不住有翻譯哈哈哈。
介紹
看了幾篇大佬的技術(shù)博客里面都介紹到,SecurityContextHolder是保存全上下文對(duì)象(SecurityContext)的地方。對(duì)于這種說法我感覺是完全不正確的,如果后面有新的認(rèn)知我會(huì)回來進(jìn)行修改。
我認(rèn)為SecurityContextHolder只是為SecurityContext提供一種存儲(chǔ)策略,只是主導(dǎo)了他的存儲(chǔ)方式及地址。
使用中我們表面上看起來SecurityContext的存儲(chǔ)都是通過SecurityContextHolder在控制人們就習(xí)以為常的說成了Security Context存儲(chǔ)在了SecurityContextHolder中,至于真相我們看源碼慢慢分析。
public class SecurityContextHolder {
// ~ Static fields/initializers
// =====================================================================================
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
public static final String MODE_GLOBAL = "MODE_GLOBAL";
public static final String SYSTEM_PROPERTY = "spring.security.strategy";
private static String strategyName = System.getProperty(SYSTEM_PROPERTY);
private static SecurityContextHolderStrategy strategy;
private static int initializeCount = 0;
首先從源碼可以看到SecurityContextHolder提供了一個(gè)SecurityContextHolderStrategy存儲(chǔ)策略進(jìn)行上下文的存儲(chǔ),進(jìn)入到Security ContextHolderStrategy接口,由下圖我們可以清晰的看到其總共有三個(gè)實(shí)現(xiàn)類。
分別對(duì)應(yīng)三種存儲(chǔ)策略,這里我不知道為什么99的文章都只說了threadlocal和global兩種。
要不是看了下實(shí)現(xiàn)類我都信了。

就跟字面意思一樣三種策略分別對(duì)應(yīng)threadlocal,global,InheritableThreadLocal三種方式。
繼續(xù)回到源碼我們?cè)敿?xì)看這三種方式。
private static void initialize() {
if (!StringUtils.hasText(strategyName)) {
// 如果沒有設(shè)置自定義的策略,就采用MODE_THREADLOCAL模式
strategyName = MODE_THREADLOCAL;
}
// ThreadLocal策略
if (strategyName.equals(MODE_THREADLOCAL)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
}
// 采用InheritableThreadLocal,它是ThreadLocal的一個(gè)子類
else if (strategyName.equals(MODE_INHERITABLETHREADLOCAL)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
}
// 全局策略,實(shí)現(xiàn)方式就是static SecurityContext contextHolder
else if (strategyName.equals(MODE_GLOBAL)) {
strategy = new GlobalSecurityContextHolderStrategy();
}
else {
// Try to load a custom strategy 自定義的策略,通過返回創(chuàng)建出
try {
Class<?> clazz = Class.forName(strategyName);
Constructor<?> customStrategy = clazz.getConstructor();
strategy = (SecurityContextHolderStrategy) customStrategy.newInstance();
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
}
}
initializeCount++;
}
由源碼我們可以得出SecurityContextHolder 默認(rèn)使用的是THREADLOCAL模式,光從名字看感覺就是存儲(chǔ)在threadlocal中的策略到底是不是我們?nèi)ピ创a中分別一探究竟:
ThreadLocalSecurityContextHolderStrategy
final class ThreadLocalSecurityContextHolderStrategy implements
SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
。。。。。。
}
InheritableThreadLocalSecurityContextHolderStrategy
final class InheritableThreadLocalSecurityContextHolderStrategy implements
SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
private static final ThreadLocal<SecurityContext> contextHolder = new InheritableThreadLocal<>();
。。。。。。
}
GlobalSecurityContextHolderStrategy
final class GlobalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
// ~ Static fields/initializers
// =====================================================================================
private static SecurityContext contextHolder;
。。。。。。
}
到這里那些說SecurityContext存儲(chǔ)在SecurityContextHolder的大佬們我認(rèn)為應(yīng)該是不嚴(yán)謹(jǐn)?shù)摹?/p>
默認(rèn)是將SecurityContext存儲(chǔ)在threadlocal中,可能是spring考慮到目前大多數(shù)為BS應(yīng)用,一個(gè)應(yīng)用同時(shí)可能有多個(gè)使用者,每個(gè)使用者又對(duì)應(yīng)不同的安全上下,Security Context Holder為了保存這些安全上下文。
缺省情況下,使用了ThreadLocal機(jī)制來保存每個(gè)使用者的安全上下文。
因?yàn)槿笔∏闆r下根據(jù)Servlet規(guī)范,一個(gè)Servlet request的處理不管經(jīng)歷了多少個(gè)Filter,自始至終都由同一個(gè)線程來完成。這樣就很好的保證了其安全性。
但是當(dāng)我們開發(fā)的是一個(gè)CS本地應(yīng)用的時(shí)候,這種模式就不太適用了。
spring早早的就考慮到了這種情況,這個(gè)時(shí)候我們就可以設(shè)置為Global模式僅使用一個(gè)變量來存儲(chǔ)SecurityContext。比如還有其他的一些應(yīng)用會(huì)有自己的線程創(chuàng)建,并且希望這些新建線程也能使用創(chuàng)建者的安全上下文。
這種效果,我們就可以通過將SecurityContextHolder配置成MODE_INHERITABLETHREADLOCAL策略達(dá)到。
SecurityContext又到底是個(gè)什么東西呢?稍后新開一遍學(xué)習(xí)記錄。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java 實(shí)戰(zhàn)練手項(xiàng)目之酒店管理系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個(gè)酒店管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
IDEA中JetBrains Mono字體的正確安裝姿勢(shì)
在 JetBrains Mono 的設(shè)計(jì)階段,它就充分考慮到了長(zhǎng)時(shí)間工作可能導(dǎo)致的眼睛疲勞問題,比如字母的大小和形狀、空間量、自然等寬平衡、不必要的細(xì)節(jié)、連字、以及難以區(qū)分的符號(hào)等,從而最終設(shè)計(jì)出了這么一款字體2021-06-06
IDEA代碼規(guī)范&質(zhì)量檢查的實(shí)現(xiàn)
這篇文章主要介紹了IDEA代碼規(guī)范&質(zhì)量檢查的實(shí)現(xiàn),文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Java concurrency集合之CopyOnWriteArraySet_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
CopyOnWriteArraySet基于CopyOnWriteArrayList實(shí)現(xiàn),其唯一的不同是在add時(shí)調(diào)用的是CopyOnWriteArrayList的addIfAbsent(若沒有則增加)方法2017-06-06
Spring Validator從零掌握對(duì)象校驗(yàn)的詳細(xì)過程
SpringValidator學(xué)習(xí)指南從零掌握對(duì)象校驗(yàn),涵蓋Validator接口、嵌套對(duì)象處理、錯(cuò)誤代碼解析等核心概念,幫助開發(fā)者實(shí)現(xiàn)數(shù)據(jù)校驗(yàn)的規(guī)范與高效,本文詳細(xì)介紹Spring Validator從零掌握對(duì)象校驗(yàn),感興趣的朋友一起看看吧2025-02-02
SpringBoot使用@Async注解可能會(huì)遇到的8大坑點(diǎn)匯總
SpringBoot中,@Async注解可以實(shí)現(xiàn)異步線程調(diào)用,用法簡(jiǎn)單,體驗(yàn)舒適,但是你一定碰到過異步調(diào)用不生效的情況,今天,我就列出90%的人都可能會(huì)遇到的8大坑點(diǎn),需要的朋友可以參考下2023-09-09
MybatisPlus整合Flowable出現(xiàn)的坑及解決
這篇文章主要介紹了MybatisPlus整合Flowable出現(xiàn)的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03

