最近想給自己的小系統搭建一個登錄認證服務,最初是想着一套oauth2權鑒就可以,但是發現這個oauth2只是權鑒,具體的登錄認證需要由 SpringSecurity來進行實現。
也就是說SpringSecurity 主要就是用來進行用戶名、密碼認證的登錄框架
然后看了一下 SpringSecurity,發現之前用過,但是只是用過,具體的流程不清楚。
趁這個機會,將SpringSecurity源碼大體的整理看一下。
概述
SpringSecurity 的功能就是 認證、授權、防止偽造登錄
其核心就是一組過濾鏈,通過各種的過濾器對請求進行過過濾,然后放行符合規則的請求。
具體流程可以看下圖:

流程
從上面我們可以清楚的認知到,我們的請求是由多個過濾器過濾之后才能訪問到具體的api 的,那么各個過濾器是怎么進行的合作的呢?
這章只說登錄認證

1. AbstractAuthenticationProcessingFilter 過濾器
首先是 SpringSecurity一系列過濾器,然后走到AbstractAuthenticationProcessingFilter 這個過濾器,這個過濾器就是一個模版,主要是用來進行 識別是否是 認證請求,然后將不是認證請求到給到下一個過濾器的實現類(就是一組過濾器的串聯)
這個過濾器中,主要的是doFilter方法,這個方法中requiresAuthentication 識別是否是認證請求,如果是,那么交給 attemptAuthentication 這個方法去處理,但是attemptAuthentication方法是抽象方法,需要子類去實現,然后我們就找到UsernamePasswordAuthenticationFilter 過濾器,也就是attemptAuthentication方法的具體實現類,也就是AbstractAuthenticationProcessingFilter的子類。
流程可以看圖
2. UsernamePasswordAuthenticationFilter 過濾器
它實際上是處理 認證請求的過濾器,AbstractAuthenticationProcessingFilter的子類,真正實現類attemptAuthentication方法。
attemptAuthentication方法中,除了進行對請求中用戶名、密碼參數的處理外,核心的一行是this.getAuthenticationManager().authenticate(authRequest); 這個就是用 選擇合適的 驗證類來進行驗證,從名稱我們也能看出AuthenticationManager 是一個管理類,這個里面有多個AuthenticationProvider 對象。選擇合適的驗證就行。
但是這個getAuthenticationManager是一個接口,需要找到真正的實現類才行。
流程可以看圖
3. ProviderManager 管理類
這個類就是真正選擇具體的認證 類,authenticate方法遍歷認證類,然后將請求中的用戶名、密碼進行驗證的。
從上圖可以看到,它就是對所有的認證類遍歷,選擇合適的進行。
provider.authenticate(authentication); 就是它的方法的核心代碼。
但是AuthenticationProvider 也是一個接口,需要找其真正的實現類。
4. AbstractUserDetailsAuthenticationProvider(抽象類)
authenticate 方法的具體實現是AbstractUserDetailsAuthenticationProvider類實現的。
我們具體看authenticate 方法,然后發現這個方法,就是先去內存中檢查,是否有這個用戶名 和密碼,如果沒有在去調用retrieveUser方法,查找這個 用戶名 和密碼。

然后找到之后,在去調用additionalAuthenticationChecks 方法去驗證 持久化中的(內存或者retrieveUser方法找到的用戶名和密碼)是否和請求中的用戶名密碼一致,如果是一致的,那么就調用additionalAuthenticationChecks 方法去驗證。
但是retrieveUser 和 additionalAuthenticationChecks 方法都是抽象方法,具體的實現,是子類進行的。
5. DaoAuthenticationProvider(子類)
這個是AbstractUserDetailsAuthenticationProvider 的子類,實現了retrieveUser 和 additionalAuthenticationChecks 方法,我們可以看到兩個方法:

注意看 this.getUserDetailsService().loadUserByUsername(username); 這個方法不就是我們自定義 獲取真正的(也就是持久化 中)用戶名 和 密碼 時需要重寫的方法么,返回的user 類,不就是UserDetails 這個類的子類么。
而且,我們在看additionalAuthenticationChecks 這個方法,不就是調用PasswordEncoder 類中的matches 方法去對比的么

至此,我們就將 SpringSecurity 用戶登錄驗證流程走通了。
我們將這個進行一個串聯
6. 串聯

這里我們就將 SpringSecurity 中 用戶驗證 流程串聯完成了,我們依據這個可以寫定制一個流程去進行。下一章是 SpringSecurity 認證的 實踐。


