1. 程式人生 > >SpringSecurity認證流程詳解

SpringSecurity認證流程詳解

SpringSecurity基本原理

在之前的文章《SpringBoot + Spring Security 基本使用及個性化登入配置》中對SpringSecurity進行了簡單的使用介紹,基本上都是對於介面的介紹以及功能的實現。 這一篇文章嘗試從原始碼的角度來上對使用者認證流程做一個簡單的分析。
在具體分析之前,我們可以先看看SpringSecurity的大概原理:
SpringSecurity基本原理
其實比較簡單,主要是通過一系列的Filter對請求進行攔截處理。

認證處理流程說明

我們直接來看UsernamePasswordAuthenticationFilter類,

public class UsernamePasswordAuthenticationFilter
extends AbstractAuthenticationProcessingFilter // 登入請求認證 public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
// 判斷是否是POST請求 if (this.postOnly && !request.getMethod().equals("POST")) { throw
new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); } else { // 獲取使用者,密碼 String username = this.obtainUsername(request); String password = this.obtainPassword(request); if (username == null) { username = ""
; } if (password == null) { password = ""; } username = username.trim(); // 生成Token, UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); this.setDetails(request, authRequest); // 進一步驗證 return this.getAuthenticationManager().authenticate(authRequest); } } }

attemptAuthentication方法中,主要是進行username和password請求值的獲取,然後再生成一個UsernamePasswordAuthenticationToken 物件,進行進一步的驗證。
不過我們可以先看看UsernamePasswordAuthenticationToken 的構造方法

public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
    // 設定空的許可權
    super((Collection)null);
    this.principal = principal;
    this.credentials = credentials;
    // 設定是否通過了校驗
    this.setAuthenticated(false);
}

其實UsernamePasswordAuthenticationToken是繼承於Authentication,該物件在上一篇文章中有提到過,它是處理登入成功回撥方法中的一個引數,裡面包含了使用者資訊、請求資訊等引數。

所以接下來我們看

this.getAuthenticationManager().authenticate(authRequest);

這裡有一個AuthenticationManager,但是真正呼叫的是ProviderManager

public class ProviderManager implements AuthenticationManager, MessageSourceAware, InitializingBean {
    public Authentication authenticate(Authentication authentication) throws AuthenticationException { 
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        boolean debug = logger.isDebugEnabled();
        Iterator var6 = this.getProviders().iterator();

        while(var6.hasNext()) {
            AuthenticationProvider provider = (AuthenticationProvider)var6.next();
            // 1.判斷是否有provider支援該Authentication
            if (provider.supports(toTest)) {
                // 2. 真正的邏輯判斷
                result = provider.authenticate(authentication);
            }
    }
}
  1. 這裡首先通過provider判斷是否支援當前傳入進來的Authentication,目前我們使用的是UsernamePasswordAuthenticationToken,因為除了帳號密碼登入的方式,還會有其他的方式,比如SocialAuthenticationToken。
  2. 根據我們目前所使用的UsernamePasswordAuthenticationToken,provider對應的是DaoAuthenticationProvider
public Authentication authenticate(Authentication authentication) throws AuthenticationException {   
    UserDetails user = this.userCache.getUserFromCache(username);
    if (user == null) {
        cacheWasUsed = false;
        // 1.去獲取UserDetails
        user = this.retrieveUser(username, (UsernamePasswordAuthenticationToken)authentication);
    }

    try {
        // 2.使用者資訊預檢查
        this.preAuthenticationChecks.check(user);
        // 3.附加的檢查(密碼檢查)
        this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
    } catch (AuthenticationException var7) {       
    }
    // 4.最後的檢查
    this.postAuthenticationChecks.check(user);
    // 5.返回真正的經過認證的Authentication 
    return this.createSuccessAuthentication(principalToReturn, authentication, user);
}
  1. 去呼叫自己實現的UserDetailsService,返回UserDetails
  2. 對UserDetails的資訊進行校驗,主要是帳號是否被凍結,是否過期等
  3. 對密碼進行檢查,這裡呼叫了PasswordEncoder
  4. 檢查UserDetails是否可用。
  5. 返回經過認證的Authentication

這裡的兩次對UserDetails的檢查,主要就是通過它的四個返回boolean型別的方法。
經過資訊的校驗之後,通過UsernamePasswordAuthenticationToken的構造方法,返回了一個經過認證的Authentication。

拿到經過認證的Authentication之後,會再去呼叫successHandler。或者未通過認證,去呼叫failureHandler。

認證結果如何在多個請求之間共享

再完成了使用者認證處理流程之後,我們思考一下是如何在多個請求之間共享這個認證結果的呢?
因為沒有做關於這方面的配置,所以可以聯想到預設的方式應該是在session中存入了認證結果。
那麼是什麼時候存放入session中的呢?
我們可以接著認證流程的原始碼往後看,在通過attemptAuthentication方法後,如果認證成功,會呼叫successfulAuthentication,該方法中,不僅呼叫了successHandler,還有一行比較重要的程式碼

SecurityContextHolder.getContext().setAuthentication(authResult);

SecurityContextHolder是對於ThreadLocal的封裝。 ThreadLocal是一個執行緒內部的資料儲存類,通過它可以在指定的執行緒中儲存資料,資料儲存以後,只有在指定執行緒中可以獲取到儲存的資料,對於其他執行緒來說則無法獲取到資料。 更多的關於ThreadLocal的原理可以看看我以前的文章。

一般來說同一個介面的請求和返回,都會是在一個執行緒中完成的。我們在SecurityContextHolder中放入了authResult,再其他地方也可以取出來的。
最後就是在SecurityContextPersistenceFilter中取出了authResult,並存入了session

SecurityContextPersistenceFilter也是一個過濾器,它處於整個Security過濾器鏈的最前方,也就是說開始驗證的時候是最先通過該過濾器,驗證完成之後是最後通過。

獲取認證使用者資訊

/**
 * 獲取當前登入的使用者
 * @return 完整的Authentication
 */
@GetMapping("/me1")
public Object currentUser() {
    return SecurityContextHolder.getContext().getAuthentication();
}

@GetMapping("/me2")
public Object currentUser(Authentication authentication) {
    return authentication;
}

/**
 * @param userDetails
 * @return 只包含了userDetails
 */
@GetMapping("/me3")
public Object cuurentUser(@AuthenticationPrincipal UserDetails userDetails) {
    return userDetails;
}

相關推薦

SpringSecurity認證流程

SpringSecurity基本原理 在之前的文章《SpringBoot + Spring Security 基本使用及個性化登入配置》中對SpringSecurity進行了簡單的使用介紹,基本上都是對於介面的介紹以及功能的實現。 這一篇文章嘗試從原始碼的角度

Kerberos認證流程

Kerberos是誕生於上個世紀90年代的計算機認證協議,被廣泛應用於各大作業系統和Hadoop生態系統中。瞭解Kerberos認證的流程將有助於解決Hadoop叢集中的安全配置過程中的問題。為此,本文根據最近閱讀的一些材料,詳細介紹Kerberos認證流程。歡迎斧正!Ke

SpringSecurity認證流程原始碼級

開發十年,就只剩下這套架構體系了! >>>   

域名註冊、域名實名認證、域名解析流程

結算 郵件收發 域名解析 ans 小夥伴 ttl .com 添加 運營商 1、域名註冊流程詳解首先登陸阿裏雲網站 www.aliyun.com 點擊產品,選擇域名註冊(左下角藍色字體) 然後來到此頁面 在輸入框中填入你想要註冊的域名產看是否已經被註冊 如:shiyan

Linux啟動流程

linux 詳解 啟動流程 grub mbr 內核 linux啟動流程第一部分 Linux啟動基礎知識1.1 linux centos6.8啟動流程圖 BIOS加電自檢à加載MBRà加載啟動grubà加載內核à啟動/sbin/i

CentOS安裝流程

菜鳥取經路之linux系統安裝 對於剛剛接觸Linux的人們來說,遇到的第一個問題便是如何快速安裝一個Linux系統。我初次接觸 Linux時也是摸索許久才安裝成功。鑒於此,今天就給大家帶來完整詳細的Linux安裝過程。一、準備安裝所需的軟件 1、VMwareWorkstation

SSL協議握手工作流程(雙向HTTPS流程)

包含 style strong 雙向認證 包括 返回 情況 身份認證 ssl 參考學習文檔:http://www.cnblogs.com/jifeng/archive/2010/11/30/1891779.html SSL協議的工作流程: 服務器認證階段: 1)客戶端向服務

centos6啟動流程

linux centos6 當我們在平常的工作學習的環境中使用linux時,我們只需要按一下開機鍵,系統就會自動為我們加載好相關配置,然後為我們打開操作界面,那麽在這個過程中究竟都發生了什麽,如果系統突然起不來了,那麽到底是啟動時的哪一部分發生了錯誤呢,下面,我們就來看看linux中的centos6啟動的

iOS APP上架流程

復制 存儲 iphone6 調試 5.1 編輯 gre 9.png 待審核 iOS APP上架流程詳解 青蔥烈馬 2016.04.28 前言:作為一名 iOS 開發工程師, APP 的上架是必備技能. iOS 上架的流程主要可以簡單總結為: 一個包,兩個

微信小程序支付及退款流程

後臺 class receive 字典序 混亂 md5 package 清晰 超時時間 微信小程序的支付和退款流程 近期在做微信小程序時,涉及到了小程序的支付和退款流程,所以也大概的將這方面的東西看了一個遍,就在這篇博客裏總結一下。 首先說明一下,微信小程序支付的主要邏輯

SpringMVC框架及基本工作流程

req isp 需要 用戶 數據呈現 工作流 esp servle 組件映射 傳統原生的JSP+Servlet在開發上過程上雖然簡單明了,JSP頁面傳遞數據到Servlet,Servlet整理數據(邏輯開發)或者從數據庫提取數據接著再轉發到JSP頁面上,但是其似乎只能止步於

k8s認證授權

k8s認證授權理解認證授權1.1 為什麽要認證想理解認證,我們得從認證解決什麽問題、防止什麽問題的發生入手。 防止什麽問題呢?是防止有人入侵你的集群,root你的機器後讓我們集群依然安全嗎?不是吧,root都到手了,那就為所欲為,防不勝防了。 其實網絡安全本身就是為了解決在某些假設成立的條件下如何防範的問題。

Struts2框架執行流程

Struts2框架執行流程詳解1. Struts2源碼導入對於struts2框架它的源代碼我們主要使用三部分 struts2核心部分源代碼 org.apache.struts2xxsrc\core\src\main\java struts2的xwork核心部分源代碼src\xwork-core\src\m

Flink1.6系列之—Flink on yarn流程

端口 準備 -a 根據 images mas info 使用 臨時 本篇我們介紹下,Flink在YARN上運行流程: 當開始一個新的Flink yarn 會話時,客戶端首先檢查所請求的資源(containers和內存)是否可用。如果資源夠用,之後,上傳

linux開機流程

Linux作業系統的開機流程詳解 開機需要十步 第一步:開機自檢(BIOS)就是開始工作之前先對自己的工具進行檢查是否正常,如果正常那就可以進行接下來的步驟假如步正常就得檢測哪裡的問題進行處理。BIOS其實就是主機板上的一給自檢程式,開機先對主機板上自帶的和外接的一些開機必備的裝置進行檢測,像CPU,顯示

iOS 企業版賬號打包及釋出-圖文流程

每一版Xcode升級之後,Archieve打包的介面都會略有調整,今天我們來講一下Xcode9的企業版打包流程。 假設現在我們已有蘋果企業版開發者賬號 $299的,並且已經建立好證書、說明檔案等前期準備工作。下面我們從Archieve說起。 一、打包 1、Product-Archieve

Hyperledger Fabric 交易背書的基本工作流程

本文內容精選自華章鮮讀專欄《Hyperledger-Fabric-原始碼分析與深入解讀》一書第二章“架構分析”。   《Hyperledger-Fabric-原始碼分析與深入解讀》紙書預計出版時間:2018年9月 華章鮮讀上線:2018年7月(按章更新,紙書出版前更

Hive UDF函式編寫流程

參考官網: https://cwiki.apache.org/confluence/display/Hive/HivePlugins     新增hive UDF函式 https://cwiki.apache.org/confluence/displ

TCP傳送資料流程

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

Linux 伺服器啟動流程

啟動第一步--載入 BIOS 當你開啟計算機電源,計算機會首先載入 BIOS 資訊,BIOS 資訊是如此的重要,以至於計算機必須在最開始就找到它。這是因為 BIOS 中包含了 CPU 的相關資訊、裝置啟動順序信息、硬碟資訊、記憶體資訊、時鐘資訊、PnP 特性等等。在此之後,計算機心裡就有譜了,知道應該去讀取