11.SpringSecurity-認證流程源碼級詳解

前言

瞭解SpringSecurity更多功能前,咱們先了解下其認證流程源碼 前端

咱們在前面幾節中說了SpringSecurity的個性化用戶認證流程:
image.png
自定義用戶認證邏輯:
image.pngspring

咱們上面都是去實現Spring給咱們的接口,好比自定義用戶認證邏輯:
實現UserDetails、UserDetailsService、接口;
在個性化用戶認證流程時候實現SpringSecurity自帶的:登陸失敗實現:AuthenticationFailureHandler;登陸成功實現:
AuthenticationSuccessHandler接口。
目前頭腦裏就是碎片化的登陸認證流程,只知道登陸成功怎麼作,登陸失敗後如何作?可是在Spring裏面是怎樣把全部邏輯串起來的?瀏覽器

內容

認證流程源碼級詳解主要講解如下3點:微信

  1. 認證處理流程說明
  2. 認證結果如何在多個請求之間共享
  3. 獲取認證用戶信息

1. 認證處理流程說明

image.png

咱們以表單認證爲例:從發起認證請求到認證過濾器,接着認證成功後,響應從認證過濾器返回的整個過程。SpringSecurity作了什麼,設計到了哪些類?他們之間如何調用?
SpringSecurity認證流程中涉及到的主要的類和接口以下:
image.pngsession

咱們按照上面圖示剖析源碼: app

應用debug啓動,瀏覽器登陸:
image.pngide

image.png
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);函數

image.png
super((Collection)null);//調用父類構造函數;
image.pngui

AuthenticationManager:自身並不包含驗證邏輯,做用用來管理下面的AuthenticationProvider;AuthenticationManager下面有不少實現類,最後是在
ProviderManager:
image.png
image.pngthis

ProviderManager在public Authentication authenticate(Authentication authentication)會進行for循環,獲取全部AuthenticationProvider,全部校驗邏輯是在AuthenticationProvider裏面的,爲何這裏是一個集合:是由於不一樣的登陸方式其認證邏輯是不同的,咱們如今是用戶名密碼登陸,是須要去校驗密碼。若是是微信登陸,則又是不同的。AuthenticationManager做用就是把全部AuthenticationProvider蒐集起來,認證時候,挨個去問,你當前的provider支不支持我如今的的登陸方式(其實就是作循環,而後調用supports方法)。
image.png

image.png

而後進入DaoAuthenticationProvider,DaoAuthenticationProvider會調用其父類的AbstractUserDetailsAuthenticationProvider的

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider

authenticate方法
image.png

這裏和咱們自定義認證邏輯上符合了,咱們自定義認證邏輯時候就是實現了一個UserDetailsService接口。

image.png

預檢查---->檢查UserDetails的3個boolean:
image.png
附加檢查:在DaoAuthenticationProvider--->檢查密碼是否匹配
image.png

而後進行後檢查:後檢查主要檢查4個boolean中最後一個:
image.png

若是上面檢驗所有沒問題的話,就認爲認證是合法的,而後咱們再建立一個已經受權的Authentication
image.png
image.png

image.png
最後就會按照下圖鏈返回:一直到:UsernamePasswordAuthenticationFilter
image.png

再往下: AbstractAuthenticationProcessingFilter
image.png

登陸成功以後走到咱們自定義的成功處理器:
image.png

SecurityContextHolder.getContext().setAuthentication(authResult);
image.png

自定義成功處理器:
image.png
登陸成功後:把認證信息打印出去:

response.getWriter().write(objectMapper.writeValueAsString(authentication));

try-catch補貨時候,若是有異常拋出就會執行:
image.png

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

多個請求共享確定是放到session裏,那麼SpringSecurity是何時?什麼東西放到了session裏面?何時又從session裏面讀取出來的?
在這個過程當中涉及到以下右邊類:

image.png

AbstractAuthenticationProcessingFilter裏面successfulAuthentication有一個:

SecurityContextHolder.getContext().setAuthentication(authResult);

實際上是把咱們認證成功的Authentication放到咱們的: SecurityContext裏面,而後SecurityContext放到SecurityContextHolder裏面。

image.png
SecurityContext其實很簡單,他就是一個接口,其惟一的實現類是:

image.png

package org.springframework.security.core.context;

import org.springframework.security.core.Authentication;

public class SecurityContextImpl implements SecurityContext {
    private static final long serialVersionUID = 420L;
    private Authentication authentication;

    public SecurityContextImpl() {
    }

    public boolean equals(Object obj) {
        if (obj instanceof SecurityContextImpl) {
            SecurityContextImpl test = (SecurityContextImpl)obj;
            if (this.getAuthentication() == null && test.getAuthentication() == null) {
                return true;
            }

            if (this.getAuthentication() != null && test.getAuthentication() != null && this.getAuthentication().equals(test.getAuthentication())) {
                return true;
            }
        }

        return false;
    }

    public Authentication getAuthentication() {
        return this.authentication;
    }

    public int hashCode() {
        return this.authentication == null ? -1 : this.authentication.hashCode();
    }

    public void setAuthentication(Authentication authentication) {
        this.authentication = authentication;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(super.toString());
        if (this.authentication == null) {
            sb.append(": Null authentication");
        } else {
            sb.append(": Authentication: ").append(this.authentication);
        }

        return sb.toString();
    }
}

SecurityContext說明:咱們看SecurityContextImpl源碼知道,其實他就是對Authentication的包裝,而且重寫其hashCode和equals,保證其惟一性。

SecurityContextHolder:他其實是ThreaadLocal封裝。ThreaadLocal是跟線程綁定的一個Map,在這個線程裏面存放的東西能夠在另外一個線程讀取出來,你能夠理解爲:線程的全局變量。

image.png

最後:SecurityContextHolder會交給SecurityContextPersistenceFilter過濾器:他的位置在過濾器鏈的最前端:

image.png

SecurityContextPersistenceFilter過濾器做用:

  1. 當請求進來的時候:檢查session裏面是否有SecurityContext,若是有將其拿出來,而後放到線程裏面,若是沒有就過去了;響應時候:從線程裏面(由於其在過濾器鏈上最前端,請求先通過此過濾器,而後最後會從這個過濾器出去)
  2. 當整個請求響應回來之後:最後一個過他的時候,他檢查線程,若是線程裏面有:SecurityContext,就拿出來放到session裏面去。

這樣不一樣的請求能夠從線程裏面拿到認證信息。拿到之後放到線程裏面,由於請求和響應是在一個線程中。

3.獲取認證用戶信息

咱們如何用SecurityContext獲取用戶信息。咱們在Spring-Security-demo 裏面的controller裏面獲取用戶信息

image.png

瀏覽器訪問:
image.png

咱們也能夠經過SpringMvc本身作的注入查找,咱們修改以下:
image.png

也能夠要求放回用戶詳情信息,其餘信息不要了。
image.png

相關文章
相關標籤/搜索