文章目錄
- 一、認證流程
- 二、多個請求共享認證信息
- 三、獲取用戶認證信息
在前面的六章中,介紹了 Spring Security 的基礎使用,在繼續深入向下的學習前,有必要理解清楚 Spring Security 的認證流程,這樣才能理解為什么要這樣寫代碼,也方便后續的擴展。
一、認證流程
上圖是 Spring Security 認證流程的一部分,下面的講解以上圖為依據。
(1) 用戶發起表單登錄請求后,首先進入 UsernamePasswordAuthenticationFilter
:
在 UsernamePasswordAuthenticationFilter 中根據用戶輸入的用戶名、密碼構建了 UsernamePasswordAuthenticationToken,並將其交給 AuthenticationManager 來進行認證處理。
AuthenticationManager 本身不包含認證邏輯,其核心是用來管理所有的 AuthenticationProvider,通過交由合適的 AuthenticationProvider 來實現認證。
(2) 下面跳轉到了 ProviderManager ,該類是 AuthenticationManager 的實現類:
我們知道不同的登錄邏輯它的認證方式是不一樣的,比如我們表單登錄需要認證用戶名和密碼,但是當我們使用三方登錄時就不需要驗證密碼。
Spring Security 支持多種認證邏輯,每一種認證邏輯的認證方式其實就是一種 AuthenticationProvider。通過 getProviders() 方法就能獲取所有的 AuthenticationProvider,通過 provider.supports() 來判斷 provider 是否支持當前的認證邏輯。
當選擇好一個合適的 AuthenticationProvider 后,通過 provider.authenticate(authentication) 來讓 AuthenticationProvider 進行認證。
(3) 傳統表單登錄的 AuthenticationProvider 主要是由 AbstractUserDetailsAuthenticationProvider 來進行處理的,我們來看下它的 authenticate()方法。
首先通過 retrieveUser() 方法讀取到數據庫中的用戶信息:
user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);
retrieveUser() 的具體實現在 DaoAuthenticationProvider
中,代碼如下:
當我們成功的讀取 UserDetails
后,下面開始對其進行認證:
在上圖中,我們可以看到認證校驗分為 前校驗、附加校驗和后校驗,如果任何一個校驗出錯,就會拋出相應的異常。所有校驗都通過后,調用 createSuccessAuthentication()
返回認證信息。
在 createSuccessAuthentication 方法中,我們發現它重新 new 了一個 UsernamePasswordAuthenticationToken,因為到這里認證已經通過了,所以將 authorities 注入進去,並設置 authenticated 為 true,即需要認證。
(4)至此認證信息就被傳遞回 UsernamePasswordAuthenticationFilter 中,在 UsernamePasswordAuthenticationFilter 的父類 AbstractAuthenticationProcessingFilter 的 doFilter() 中,會根據認證的成功或者失敗調用相應的 handler:
這里調用的 handler 實際就是在《SpringBoot集成Spring Security(6)——登錄管理》中我們在配置文件中配置的 successHandler()
和 failureHandler()
。
二、多個請求共享認證信息
Spring Security 通過 Session 來保存用戶的認證信息,那么 Spring Security 到底是在什么時候將認證信息放入 Session,又在什么時候將認證信息從 Session 中取出來的呢?
下面將 Spring Security 的認證流程補充完整,如下圖:
在上一節認證成功的 successfulAuthentication()
方法中,有一行語句:
其實就是在這里將認證信息放入 Session 中。
查看 SecurityContext 源碼,發現內部就是對 Authentication 的封裝,提供了 equals、hashcode、toString等方法,而SecurityContextHolder 可以理解為線程中的 ThreadLocal。
我們知道一個 HTTP 請求和響應都是在一個線程中執行,因此在整個處理的任何一個方法中都可以通過 SecurityContextHolder.getContext()來取得存放進去的認證信息。
從 Session 中對認證信息的處理由 SecurityContextPersistenceFilter 來處理,它位於 Spring Security 過濾器鏈的最前面,它的主要作用是:
- 當請求時,檢查 Session 中是否存在 SecurityContext,如果有將其放入到線程中。
- 當響應時,檢查線程中是否存在 SecurityContext,如果有將其放入到 Session 中。
三、獲取用戶認證信息
通過調用 SecurityContextHolder.getContext().getAuthentication()
就能夠取得認證信息:
@GetMapping("/me") @ResponseBody public Object me() { return SecurityContextHolder.getContext().getAuthentication(); }
上面的寫法有點啰嗦,我們可以簡寫成下面這種, Spring MVC 會自動幫我們從 Spring Security 中注入:
@GetMapping("/me") @ResponseBody public Object me(Authentication authentication) { return authentication; }
如果你僅想獲取 UserDetails
對象,也是可以的,寫法如下:
---------------------
作者:Jitwxs
來源:CSDN
原文:https://blog.csdn.net/yuanlaijike/article/details/84703690