SpringBoot集成Spring Security——認證流程

在前面的六章中,介紹了 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);
  • 1

retrieveUser() 的具體實現在 DaoAuthenticationProvider 中,代碼如下:

當我們成功的讀取 UserDetails 後,下面開始對其進行認證:

在上圖中,我們可以看到認證校驗分爲 前校驗附加校驗後校驗,如果任何一個校驗出錯,就會拋出相應的異常。所有校驗都通過後,調用 createSuccessAuthentication() 返回認證信息。

在 createSuccessAuthentication 方法中,我們發現它重新 new 了一個 UsernamePasswordAuthenticationToken,因爲到這裏認證已經通過了,所以將 authorities 注入進去,並設置 authenticated 爲 true,即需要認證。

(4)至此認證信息就被傳遞迴 UsernamePasswordAuthenticationFilter 中,在 UsernamePasswordAuthenticationFilter 的父類 AbstractAuthenticationProcessingFilterdoFilter() 中,會根據認證的成功或者失敗調用相應的 handler:

這裏調用的 handler 實際就是在《SpringBoot集成Spring Security(6)——登錄管理》中我們在配置文件中配置的 successHandler()failureHandler()

二、多個請求共享認證信息

Spring Security 通過 Session 來保存用戶的認證信息,那麼 Spring Security 到底是在什麼時候將認證信息放入 Session,又在什麼時候將認證信息從 Session 中取出來的呢?

下面將 Spring Security 的認證流程補充完整,如下圖:

在上一節認證成功的 successfulAuthentication()方法中,有一行語句:

SecurityContextHolder.getContext().setAuthentication(authResult);
  • 1

其實就是在這裏將認證信息放入 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();
}
  • 1
  • 2
  • 3
  • 4
  • 5

上面的寫法有點囉嗦,我們可以簡寫成下面這種, Spring MVC 會自動幫我們從 Spring Security 中注入:

@GetMapping("/me")
@ResponseBody
public Object me(Authentication authentication) {
    return authentication;
}

如果你僅想獲取 UserDetails 對象,也是可以的,寫法如下:

@GetMapping("/me")
@ResponseBody
public Object me(@AuthenticationPrincipal UserDetails userDetails) {
    return userDetails;
}

            整合了密碼、短信驗證碼、qq、人臉登陸:https://github.com/jitwxs/express
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章