新線程中獲取Spring Security認證信息

在做一些耗時的操作邏輯時,經常會通過開啓新線程進行處理。但是,直接通過new Thread()/new Runnable() 方式創建的線程中沒有用戶的認證信息(即SecurityContextHolder.getContext().getAuthentication() 返回的是 null)。這是由於 認證信息 是在請求到達後臺,經由 Security 的一系列過濾器後將 認證信息寫入到 線程本地變量中。所以,新開的線程並沒有執行過濾器的邏輯,自然沒有認證信息。
既然不會自動設置認證信息,那我們只有手動設置認證信息了。

方式一: 手動設置線程中的認證信息

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

// 新線程
Runnable runnable = new Runnable() {
	public void run() {
		// 手動設置線程中的認證信息
		SecurityContextHolder.getContext().setAuthentication(authentication);
		
		// 線程處理邏輯(後續就能獲取到認證信息)
		...
	}
};
new Thread(runnable).start();

方式二:使用 DelegatingSecurityContextRunnable 創建線程

官方文檔:https://docs.spring.io/spring-security/site/docs/5.0.16.RELEASE/reference/htmlsingle/#delegatingsecuritycontextrunnable

Spring Security 考慮到了新線程需要訪問認證信息的情況,提供了 DelegatingSecurityContextRunnable 類,通過該類構建新線程(Runnable),線程內部自然能獲取認證信息。
查看DelegatingSecurityContextRunnable的源碼, 你會發現,本質上思路和方式一是一致的。

Runnable runnable = new DelegatingSecurityContextRunnable(() -> {
  // 線程處理邏輯
  ...
});
new Thread(runnable).start();

或者

Runnable runnable = DelegatingSecurityContextRunnable.create(() -> {
  //線程處理邏輯
  ...
}, SecurityContextHolder.getContext());
new Thread(runnable).start();

另:
Spring Security 提供一個線程池DelegatingSecurityContextExecutor,線程池類所有線程公用一個認證信息。如果是執行比較通用的邏輯,並不涉及個人的認證信息。其應該是一個比較好的方案。

官方文檔:https://docs.spring.io/spring-security/site/docs/5.0.16.RELEASE/reference/htmlsingle/#delegatingsecuritycontextexecutor

reference:
官方文檔:Spring Security 對併發的支持


end

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章