Spring Security 基本認(rèn)證組件
1. 前言
在前面的章節(jié),我們介紹了 Spring Security 的基礎(chǔ)理念。本節(jié)開始我們將進(jìn)入一個新的主題「認(rèn)證」。在很多應(yīng)用系統(tǒng)中,認(rèn)證是用戶使用系統(tǒng)的第一步,認(rèn)證的目的就是系統(tǒng)對用戶身份的識別,也就是用戶回答系統(tǒng)“我是誰?”,并向系統(tǒng)證明“我就是誰”。
認(rèn)證的概念不難理解,可實現(xiàn)起來卻沒那么容易,因為相互信任本身就是一件困難的事。在計算機(jī)領(lǐng)域中,似乎很少能出現(xiàn)絕對的信任,而且驗證的復(fù)雜度與執(zhí)行效率往往是相互沖突的。我們往往要在安全性與易用性之間做取舍。像社交網(wǎng)站、電子郵箱這類的應(yīng)用,一般只要輸入用戶名密碼就可以了,并且不用每次都進(jìn)行登錄。但是如果是安全性要求很高的應(yīng)用,僅僅有用戶名密碼就不夠了,可能還需要 U-Key、電子證書等等。
面對不同的認(rèn)證方式,我們的業(yè)務(wù)系統(tǒng)如何實現(xiàn)快速集成?如何將鑒權(quán)過程標(biāo)準(zhǔn)化?多個子業(yè)務(wù)系統(tǒng)之間如何聯(lián)動?我的認(rèn)證結(jié)果是否能安全的保存下來?可見想把認(rèn)證過程標(biāo)準(zhǔn)化,并能適配大多數(shù)場景也不是件輕松的事。好在 Spring Security 替我們完成了這一挑戰(zhàn)。
本節(jié)將重點討論 Spring Security 的認(rèn)證基礎(chǔ)——核心認(rèn)證組件。
本節(jié)所指組件是 Spring Security 在程序執(zhí)行過程中的重要單元,也就是我們在開發(fā)過程中的常用對象或結(jié)構(gòu)。Spring Security 的運轉(zhuǎn)便是通過這些組件的配合實現(xiàn)的。
2. 組件總覽
Spring Security 的認(rèn)證組件可以分為三個組別:
- 存儲單元。包括:
Authentication
、GrantedAuthority
、SecurityContextHolder
、SecurityContext
; - 認(rèn)證管理。包括:
AuthenticationManager
、ProviderManager
、AuthenticationProvider
、AuthenticationEntryPoint; - 流程管理。
AbstractAuthenticationProcessingFilter
及其子類。
具體組件名稱及作用如下:
組件名 | 簡述 |
---|---|
SecurityContextHolder | SecurityContextHolder 用于維護(hù) SpringContext。 |
SecurityContext | SecurityContext 用來存儲當(dāng)前認(rèn)證用戶的信息。 |
Authentication | 維護(hù)用戶用于認(rèn)證的信息。 |
GrantedAuthority | 認(rèn)證用戶的權(quán)限信息比如角色、范圍等等。 |
AuthenticationManager | SpringSecurity 向外提供的用于認(rèn)證的 API 集合。 |
ProviderManager | AuthenticationManager 的常見實現(xiàn)類 |
AuthenticationProvider | 用于 ProviderManager 提供認(rèn)證實現(xiàn) |
AuthenticationEntryPoint | 用于獲取用戶認(rèn)證信息 |
AbstractAuthenticationProcessingFilter | 是認(rèn)證過濾器的基礎(chǔ),用于組合認(rèn)證流程 |
3. 各組件詳細(xì)說明
3.1 存儲單元
3.1.1 Authentication
和 GrantedAuthority
Authentication
是用戶的認(rèn)證信息,該對象有三個核心屬性:
- principal。用戶的身份信息;
- credentials。用戶的認(rèn)證憑據(jù),比如密碼,通常情況下,當(dāng)用戶完成認(rèn)證后,此項內(nèi)容就會被清空;
- authorities。用戶的權(quán)限,用于更高層次的鑒權(quán)功能,通常包括角色、使用范圍等信息。該屬性基本由
GrantedAuthority
實現(xiàn)。GrantedAuthority
是在前述Authentication
對象中所指的權(quán)限信息。在開發(fā)過程中,可以通過Authentication.getAuthorities()
方法獲取。權(quán)限信息通常包括角色、范圍,或者其他擴(kuò)展內(nèi)容。
Authentication
有兩個主要作用:
- 為
AuthenticationManager
對象提供用于認(rèn)證的信息載體; - 用于獲取某個用戶的基本信息。
3.1.2 SecurityContextHolder
和 SecurityContext
SecurityContextHolder
對象是整個 Spring Security 體系的核心,它維護(hù)著SecurityContext
對象。SecurityContext
對象用于銜接SecurityContextHolder
和Authentication
對象,是對Authentication
的外層封裝。- 在 Spring Security 項目中,
SecurityContextHolder
對象是唯一的。
SecurityContextHolder
對象為 Spring Security 保存著所有認(rèn)證用戶的信息。Spring Security 本身不關(guān)心 SecurityContextHolder
如何獲取到用戶信息,一旦其中包含用戶對象,Spring Security 就認(rèn)為當(dāng)前用戶認(rèn)證成功了。
由此可知,假如我們希望 Spring Security 保持認(rèn)證通過狀態(tài),而不需要經(jīng)歷復(fù)雜的認(rèn)證過程,最直接的方式便是往 SecurityContextHolder
里寫入用戶信息。例如:
// 第一步,創(chuàng)建 SecurityContext 對象
SecurityContext context = SecurityContextHolder.createEmptyContext();
// 第二步,為 SpringContext 注入認(rèn)證信息,例如 用戶名:testUser;密碼:testPassword;角色:ROLE_TEST
Authentication authentication = new TestingAuthenticationToken("testUser", "testPassword", "ROLE_TEST");
context.setAuthentication(authentication);
// 第三步,為 SecurityContextHolder 注入當(dāng)前用戶的 SecurityContext 對象
SecurityContextHolder.setContext(context);
通過以上代碼,在 Spring Security 中加入了一個已認(rèn)證的用戶。
通過 SecurityContextHolder
對象,我們也可以獲取到當(dāng)前登錄的用戶信息。例如:
// 第一步,獲取當(dāng)前線程內(nèi)的 SecurityContext 對象
SecurityContext context = SecurityContextHolder.getContext();
// 第二步,從 SecurityContext 對象中獲取用戶認(rèn)證信息,如用戶名、密碼、權(quán)限
Authentication authentication = context.getAuthentication();
String username = authentication.getName();
Object principal = authentication.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
默認(rèn)情況下,SecurityContextHolder
使用 ThreadLocal
存儲變量信息。
3.2 認(rèn)證管理
3.2.1 AuthenticationManager
AuthenticationManager
為 Spring 過濾器提供認(rèn)證支持 API。AuthenticationManager
的實現(xiàn)形式并沒有嚴(yán)格限制,通常情況下使用 ProviderManager
。
3.2.2 ProviderManager
ProviderManager
是 AuthenticationManager
的最常用的實現(xiàn)類,它包含了一系列的 AuthenticationProvider
對象,用以判斷認(rèn)證流程是否完成、認(rèn)證結(jié)構(gòu)是否成功。
3.2.3 AuthenticationProvider
每個 ProviderManager
可以包含多個 AuthenticationProvider
,每個 AuthenticationProvider
提供一種認(rèn)證類型,例如:DaoAuthenticationProvider
可以完成「用戶名 / 密碼」的認(rèn)證,JwtAuthenticationProvider
用于完成 JWT 方式的認(rèn)證。
3.2.4 AuthenticationEntryPoint
當(dāng)一個請求包含的認(rèn)證信息不全時,比如未認(rèn)證終端訪問受保護(hù)資源時,AuthenticationEntryPoint
對象便會發(fā)揮其作用,如跳轉(zhuǎn)到登錄頁面、返回認(rèn)證要求等。
3.3 流程管理
Spring Security 用安全過濾器管理認(rèn)證流程,AbstractAuthenticationProcessingFilter
是所有認(rèn)證過濾器的基類。它完成了以下幾項內(nèi)容:
- 當(dāng)用戶提交認(rèn)證信息,
AbstractAuthenticationProcessingFilter
首先從請求信息(例如用戶名、密碼)中創(chuàng)建Authentication
對象; - 將
Authentication
對象傳遞給AuthenticationManager
對象,用于后續(xù)認(rèn)證; - 如果認(rèn)證失敗,則執(zhí)行失敗流程:
- 清空 SecurityContextHolder 對象;
- 觸發(fā)
RememberMeServices.loginFail
方法; - 觸發(fā)
AuthenticationFailureHandler
。
- 如果認(rèn)證成功,則執(zhí)行成功流程:
SessionAuthenticationStrategy
登記新的登錄;- 將
Authentication
對象設(shè)置到SecurityContextHolder
對象中,并將SecurityContext
對象保持到 Session 中; - 調(diào)用
RememberMeServices.loginSuccess
方法; ApplicationEventPublisher
發(fā)起事件InteractiveAuthenticationSuccessEvent
4. 小結(jié)
本小節(jié)主要講解了Spring Security 的主要組件:
Spring Security 的組件包含了「存儲單元」、「認(rèn)證管理」和「流程管理」三個部分;
Spring Security 的各種認(rèn)證擴(kuò)展都是基于這三個部分實現(xiàn)的。
下一節(jié)我們將討論認(rèn)證中最普遍的一種方式:“用戶名密碼認(rèn)證”。