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