登陸認證框架:SpringSecurity

張小吉發表於2022-01-02

最近想給自己的小系統搭建一個登入認證服務,最初是想著一套oauth2權鑑就可以,但是發現這個oauth2只是權鑑,具體的登入認證需要由 SpringSecurity來進行實現。
也就是說SpringSecurity 主要就是用來進行使用者名稱、密碼認證的登入框架
然後看了一下 SpringSecurity,發現之前用過,但是隻是用過,具體的流程不清楚。
趁這個機會,將SpringSecurity原始碼大體的整理看一下。

概述

SpringSecurity 的功能就是 認證、授權、防止偽造登入

其核心就是一組過濾,通過各種的過濾器對請求進行過過濾,然後放行符合規則的請求。
具體流程可以看下圖:

image

流程

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

1. AbstractAuthenticationProcessingFilter 過濾器

首先是 SpringSecurity一系列過濾器,然後走到AbstractAuthenticationProcessingFilter 這個過濾器,這個過濾器就是一個模版,主要是用來進行 識別是否是 認證請求,然後將不是認證請求到給到下一個過濾器的實現類(就是一組過濾器的串聯)
這個過濾器中,主要的是doFilter方法,這個方法中requiresAuthentication 識別是否是認證請求,如果是,那麼交給 attemptAuthentication 這個方法去處理,但是attemptAuthentication方法是抽象方法,需要子類去實現,然後我們就找到UsernamePasswordAuthenticationFilter 過濾器,也就是attemptAuthentication方法的具體實現類,也就是AbstractAuthenticationProcessingFilter的子類。

流程可以看圖
image

2. UsernamePasswordAuthenticationFilter 過濾器

它實際上是處理 認證請求的過濾器,AbstractAuthenticationProcessingFilter的子類,真正實現類attemptAuthentication方法。

attemptAuthentication方法中,除了進行對請求中使用者名稱、密碼引數的處理外,核心的一行是this.getAuthenticationManager().authenticate(authRequest); 這個就是用 選擇合適的 驗證類來進行驗證,從名稱我們也能看出AuthenticationManager 是一個管理類,這個裡面有多個AuthenticationProvider 物件。選擇合適的驗證就行。
但是這個getAuthenticationManager是一個介面,需要找到真正的實現類才行。

流程可以看圖
image

3. ProviderManager 管理類

這個類就是真正選擇具體的認證 類,authenticate方法遍歷認證類,然後將請求中的使用者名稱、密碼進行驗證的。
從上圖可以看到,它就是對所有的認證類遍歷,選擇合適的進行。
provider.authenticate(authentication); 就是它的方法的核心程式碼。
但是AuthenticationProvider 也是一個介面,需要找其真正的實現類。

4. AbstractUserDetailsAuthenticationProvider(抽象類)

authenticate 方法的具體實現是AbstractUserDetailsAuthenticationProvider類實現的。
我們具體看authenticate 方法,然後發現這個方法,就是先去記憶體中檢查,是否有這個使用者名稱 和密碼,如果沒有在去呼叫retrieveUser方法,查詢這個 使用者名稱 和密碼。
image
然後找到之後,在去呼叫additionalAuthenticationChecks 方法去驗證 持久化中的(記憶體或者retrieveUser方法找到的使用者名稱和密碼)是否和請求中的使用者名稱密碼一致,如果是一致的,那麼就呼叫additionalAuthenticationChecks 方法去驗證。

但是retrieveUser 和 additionalAuthenticationChecks 方法都是抽象方法,具體的實現,是子類進行的。

5. DaoAuthenticationProvider(子類)

這個是AbstractUserDetailsAuthenticationProvider 的子類,實現了retrieveUser 和 additionalAuthenticationChecks 方法,我們可以看到兩個方法:
image

注意看 this.getUserDetailsService().loadUserByUsername(username); 這個方法不就是我們自定義 獲取真正的(也就是持久化 中)使用者名稱 和 密碼 時需要重寫的方法麼,返回的user 類,不就是UserDetails 這個類的子類麼。

而且,我們在看additionalAuthenticationChecks 這個方法,不就是呼叫PasswordEncoder 類中的matches 方法去對比的麼
image

至此,我們就將 SpringSecurity 使用者登入驗證流程走通了。
我們將這個進行一個串聯

6. 串聯

image

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

相關文章