Spring-Security 認證流程分析及多方式登錄認證實踐

2024年2月6日 22点热度 0人点赞

1 前言

在項目開發過程中,會涉及到安全框架的配置。其中常用的就是 shiro 和 spring-security, 在本文中將介紹 spring-security 的工作流程和實踐應用,並基於此總結其使用心得和項目配置關鍵。

2 spring security 認證和權限流程

如上圖所示,一個請求在達到控制器之前,會經過一系列的過濾器
DefaultSecurityFilterChain,綠色的部分負責用戶認證,藍色的部分處理認證異常的情況,橙色的部分負責用戶的授權,最終請求到控制器的方法。在認證完成之後,會將用戶認證的結果 Authentication 放進 SecurityContextHolder 中,從中可以獲取到用戶信息。

在開始之前,需要先介紹一下其他重要的組件

SecurityContextHolder:存放身份信息的容器
Authentication:用戶信息的抽象即 security 的認證主體
AbstractAuthenticationToken: authentication 即認證主體的抽象類,
AuthenticationManager:身份認證器接口,具體實現類為 ProviderManager
AuthenticationProvider:用來處理 authentication 的認證信息
AuthenticationSuccessHandler 認證成功處理器
AuthenticationFailureHandler 認證失敗處理器

認證的流程如下所示:

1 用戶請求的接口經過 security 過濾器,按照其登錄方式適配的 authentication 封裝其認證方法,
通過 AuthenticationProvider 來進行處理認證。獲取身份信息會按照其配置provider的順序來處理。
2 通常情況下將身份信息封裝到封裝成Authentication下的實現類UsernamePasswordAuthenticationToken,
通常是用戶密碼的登錄方式。
3 通過 AuthenticationManager 身份管理器,通過其配置的provider 驗證這個UsernamePasswordAuthenticationToken 的信息。
4 認證邏輯一般是在 service 中,認證成功之後會返回認證信息,AuthenticationManager.authentication
身份管理器返回一個Authentication實例(上下文中包含權限信息,身份信息,細節信息,但是密碼會被移除)。
5 SecurityContextHolder 上下文容器會填充UsernamePasswordAuthenticationToken信息,
通過 SecurityContextHolder.getContext().setAuthentication()方法可以查看其上下文用戶信息。

上述流程具體來說,如下圖所示:

3 spring security 登錄配置

通常情況下,都是用戶名和密碼表單的方式進行登錄,同時在登錄時還會添加動態驗證碼和自動登錄的方式,具體如下圖所示:

核心配置如下所示:

1 攔截認證請求配置,這裡需要配置不需要登錄就可以訪問的資源,包括登錄頁面、請求提交頁面,靜態資源等內容。
登錄請求不攔截 permitAll
資源需要某些角色 hasRole
資源訪問需要某些權限 hasAuthority
其它的資源需要經過認證,在資源權限上的配置也可以通過註解的方式在控制器中設置
2 登錄頁面配置,需要配置登錄請求的參數,登錄頁面,登錄訪問路徑以及成功和失敗的處理器,這裡需要註意的是,
successForwardUrl 是請求轉發的方式,defaultSuccessUrl是重定向的方式。
3 退出的配置,這裡需要配置的是退出的地址以及session 和 authentication 的處理。
4 session 的管理,通常情況下在前後端不分離的情況下,需要用 session 進行管理,在前後端分離的情況下,
一般通過 token 的方式來存儲用戶信息。maximumSessions 表示最大的用戶登錄數量。
5 在處理異常的情況時,配置異常信息的處理器,通常是錯誤頁或者返回錯誤信息,accessDeniedHandler 即權限異常處理器
6 在登錄時,還會有記住我的選項,需要配置 token 的配置信息,以及記住我的參數信息。
7 csrf/cors/frameoptions/cache 的配置方式。
8 此外還會有 addFilterAt/addFilterAfter/addFilterBefore 的方式來處理自定義的過濾器。

spring-security 的其它配置如下所示,可以配置信息提示的國際化,身份認證的方式,自定義 AuthenticationManager,以及配置token 的持久化方式,這裡包括內存方式和數據庫方式。

如果登錄需要配置驗證碼的方式,則需要配置驗證碼的方式,這裡需要處理圖片驗證碼。配置密碼處理器,這裡為了方便起見,使用的是明文密碼。資源的配置可以配置靜態資源的方式,來配置靜態資源和某些接口的訪問權限。

KaptchaFilter 是用來配置驗證碼的處理,通常情況下登錄需要需要 post 的方式,對於驗證碼的處理方式,需要先生成驗證碼存儲到內存中,然後登錄時需要攜帶驗證碼參數來進行校驗而後進行下一個過濾器處理。

最後,需要註意的是,在設置登錄後成功的主頁面時,如果配置的是請求轉發的方式,其訪問方式需要和登錄處理器的 http method 保持一致。

4 spring security 表單認證流程

通過上述的表單認證配置,通過源碼分析可以得知如下的表單認證流程,具體如下圖所示:

  • 1 SecurityContextPersistenceFilter 攔截所有的請求,共享會話將 session 中的 SecurityContext 信息放進線程上下文中。
  • 2 AbstractAuthenticationProcessingFilter 判斷是否需要認證,需要認證就獲取認證 Authentication, 然後放進 session 中。
  • 3 未認證的請求由 UsernamePasswordAuthenticationFilter 創建 UsernamePasswordAuthenticationToken 進行認證,委托 ProviderManager (即 AuthenticationManager) 發起認證,由多個 AuthenticationProvider 接口列表組成進行循環處理。
  • 4 DaoAuthenticationProvider 調用 UserDetailsService 方法進行用戶查詢,最終返回 AuthenticationToken 即 UsernamePasswordAuthenticationToken
  • 5 整個過程中出現認證失敗情況交由 ExceptionTranslationFilter 進行處理,
  • 6 所有的配置攔截規則在 FilterSecurityInterceptor 中體現,判斷用戶是否有權限訪問資源。

5 手機號和郵箱驗證碼登錄

通過以上的分析,已經明晰了 spring security 認證相關的內容,通常情況下,系統的登錄不僅有賬號密碼登錄,還有手機號驗證碼、第三方授權等登錄多種方式,在接下來將要介紹的就是手機號驗證碼登錄和郵箱登錄的方式。

接下來將要重點介紹的如下:

復制代碼繼承 AbstractAuthenticationProcessingFilter,重寫 attemptAuthentication 用於獲取登錄提交參數,並校驗驗證碼是否正確
繼承 AuthenticationProvider,重寫 authenticate 方法,從數據庫查詢用戶信息,並完成 AuthenticationToken 對象封裝。
繼承 AbstractAuthenticationToken, 重寫構造方法,完成對象封裝以及授權。

如下圖所示,完成如下配置即可完成手機號驗證碼登錄的功能。

核心的兩處代碼如下圖所示: 參數的獲取以及請求的token 封裝認證。

用戶認證成功後封裝用戶信息。

除此之外,還有發送驗證碼的接口,通常驗證碼會存儲在 redis 中,通過憑證來從緩存中獲取並校驗。

手機號登錄流程如下:

  • 1 調用發送驗證碼接口,發送驗證碼,本例是在控制臺打印驗證碼。
  • 2 通過表單或者json格式都可以提交信息完成認證。
  • 3 用戶認證成功或者失敗時,通過認證成功或者失敗處理器進行處理,返回 json 數據格式或者是頁面。

終於搞懂了spring security 相關的認證流程和認證成功的處理方式,此外,還可以通過如下方式,通過註入 AuthenticationManager 調用認證方式來處理用戶認證信息。

6 總結

本文主要介紹了 spring security 登錄相關的核心配置,以及驗證碼方式登錄的實踐, spring security 作為系統開發中常用的權限認證框架,在此基礎上拓展了項目的多種登錄方式,即手機驗證碼和郵箱驗證碼的方式,同時也介紹了前後端分離與不分離情況下的差異。本文總結了 spring security 常用的使用方式,可以很好的為後續開發提供借鑒。

作者:豫州牧
鏈接:
https://juejin.cn/post/7329144188926967860