Authentication 存在哪裡?UsernamePasswordAuthenticationFilter → successfulAuthentication() → SecurityContextHolder.getContext().setAuthentication(auth)
public class SecurityContextHolder {
private static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
private static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
private static final String MODE_GLOBAL = "MODE_GLOBAL";
private static SecurityContextHolderStrategy strategy;
static { initialize(); }
private static void initialize() {
String strategyName = System.getProperty("spring.security.strategy", MODE_THREADLOCAL);
if (MODE_THREADLOCAL.equals(strategyName)) {
strategy = new ThreadLocalSecurityContextHolderStrategy();
} else if (MODE_INHERITABLETHREADLOCAL.equals(strategyName)) {
strategy = new InheritableThreadLocalSecurityContextHolderStrategy();
} else if (MODE_GLOBAL.equals(strategyName)) {
strategy = new GlobalSecurityContextHolderStrategy();
} else {
throw new IllegalStateException("Unknown strategy: " + strategyName);
}
}
public static void setContext(SecurityContext context) {
strategy.setContext(context);
}
// ... getContext, clearContext 同理委派
}
默認策略:ThreadLocalSecurityContextHolderStrategy —— 每個線程獨立副本。
public class ThreadLocalSecurityContextHolderStrategy implements SecurityContextHolderStrategy {
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
public void setContext(SecurityContext context) {
if (context == null) {
contextHolder.remove();
} else {
contextHolder.set(context);
}
}
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
// ...
}
關鍵點:ThreadLocal<SecurityContext> 保證線程隔離。
SecurityContextPersistenceFilter 在請求開始時從 Session 讀取 SecurityContext 存入 ThreadLocal,請求結束時回寫 Session。get() 看到同一對象;不同線程互不干擾,各自獨立副本。因此:請求線程 = 權限上下文邊界。用戶 A 的請求走線程 1,用戶 B 走線程 2,ThreadLocal 天然隔離。
Authentication 存入各自的 ThreadLocal。JSESSIONID Cookie 標識,同瀏覽器共享 Cookie,但線程隔離保證運行時不串。請求進入
↓
SecurityContextPersistenceFilter.doFilter()
├─ 從 HttpSession 讀取 SecurityContext → ThreadLocal.set()
↓
其他業務 Filter / Controller
├─ SecurityContextHolder.getContext().getAuthentication() ← 讀 ThreadLocal
↓
SecurityContextPersistenceFilter.doFilter() (finally 塊)
├─ ThreadLocal.get() → 寫回 HttpSession
├─ ThreadLocal.remove() 防止線程池復用洩漏
↓
響應返回
Thread 類持有 ThreadLocalMap threadLocals。ThreadLocal.set(value) 實際調用 map.set(this, value),Key 是 ThreadLocal 實例本身。remove() → Value 永不回收。Spring Security 在 finally 塊強制 remove() 正是為此。Authentication 運行時存放在 ThreadLocal<SecurityContext> 中。SecurityContextPersistenceFilter 負責「Session ⇄ ThreadLocal」雙向同步。ThreadLocal 是掌握 Spring 事務、Spring Security、Spring MVC 請求上下文等核心機制的前置知識。源碼雖多,但核心邏輯清晰:策略模式選擇存儲方式 → 默認 ThreadLocal → Filter 同步 Session → 線程隔離保證並發安全。