Spring Security 從根本上講是線程綁定的,因爲它需要使當前經過身份驗證的主體可供各種下游使用者使用。基本構件是 SecurityContext
,它可以包含一個 Authentication
(當用戶登錄時,它將是一個經過顯式 authenticated
的 Authentication
)。你始終可以通過 SecurityContextHolder
中的靜態便捷方法來訪問和操作 SeucurityContext
,而該方法又可以簡單地操作 ThreadLocal
,例如:
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
assert(authentication.isAuthenticated);
用戶應用代碼執行該操作並不常見,但是例如在你需要編寫自定義身份驗證過濾器時可能會很有用(儘管即使如此,Spring Security 中也可以使用基類來避免需要使用 SecurityContextHolder
)。
如果你需要訪問 Web 端點中當前經過身份驗證的用戶,則可以在 @RequestMapping
中使用 method 參數。例如:
@RequestMapping("/foo")
public String foo(@AuthenticationPrincipal User user) {
... // do stuff with user
}
該註解將當前的 Authentication
從 SecurityContext
中拉出,並對其調用 getPrincipal()
方法以產生 method 參數。Authentication
中的 Principal
類型取決於驗證身份驗證的 AuthenticationManager
,因此這是獲得對用戶數據的類型安全應用的有用的小技巧。
如果使用 Spring Security,則 HttpServletRequest
中的 Principal
將爲 Authentication
類型,因此你也可以直接使用它:
@RequestMapping("/foo")
public String foo(Principal principal) {
Authentication authentication = (Authentication) principal;
User = (User) authentication.getPrincipal();
... // do stuff with user
}
如果你需要編寫在不使用 Spring Security 時可以工作的代碼,那麼有時這很有用(你需要在裝入 Authentication
類時更具防禦性)。
異步處理安全方法
由於 SecurityContext
是線程綁定的,因此,如果要執行任何調用安全方法的後臺處理,例如使用 @Async
,你需要確保傳播上下文。這歸結爲將 SecurityContext
打包爲在後臺執行的任務(Runnable
、Callable
等)。Spring Security 提供了一些幫助程序,例如 Runnable
和 Callable
的包裝器。要將 SecurityContext
傳播到 @Async
方法,你需要提供 AsyncConfigurer
並確保 Executor
具有正確的類型:
@Configuration
public class ApplicationConfiguration extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
return new DelegatingSecurityContextExecutorService(Executors.newFixedThreadPool(5));
}
}