欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringSecurity認(rèn)證流程詳解

 更新時(shí)間:2018年05月25日 08:53:17   作者:whyalwaysmea  
這篇文章主要介紹了SpringSecurity認(rèn)證流程詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

SpringSecurity基本原理

在之前的文章《SpringBoot + Spring Security 基本使用及個(gè)性化登錄配置》中對(duì)SpringSecurity進(jìn)行了簡單的使用介紹,基本上都是對(duì)于接口的介紹以及功能的實(shí)現(xiàn)。 這一篇文章嘗試從源碼的角度來上對(duì)用戶認(rèn)證流程做一個(gè)簡單的分析。
在具體分析之前,我們可以先看看SpringSecurity的大概原理:

SpringSecurity基本原理

其實(shí)比較簡單,主要是通過一系列的Filter對(duì)請(qǐng)求進(jìn)行攔截處理。

認(rèn)證處理流程說明

我們直接來看UsernamePasswordAuthenticationFilter類,

public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter
  // 登錄請(qǐng)求認(rèn)證
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
    // 判斷是否是POST請(qǐng)求
    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);
      // 進(jìn)一步驗(yàn)證
      return this.getAuthenticationManager().authenticate(authRequest);
    }
  }
}

attemptAuthentication方法中,主要是進(jìn)行username和password請(qǐng)求值的獲取,然后再生成一個(gè)UsernamePasswordAuthenticationToken 對(duì)象,進(jìn)行進(jìn)一步的驗(yàn)證。

不過我們可以先看看UsernamePasswordAuthenticationToken 的構(gòu)造方法

public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
  // 設(shè)置空的權(quán)限
  super((Collection)null);
  this.principal = principal;
  this.credentials = credentials;
  // 設(shè)置是否通過了校驗(yàn)
  this.setAuthenticated(false);
}

其實(shí)UsernamePasswordAuthenticationToken是繼承于Authentication,該對(duì)象在上一篇文章中有提到過,它是處理登錄成功回調(diào)方法中的一個(gè)參數(shù),里面包含了用戶信息、請(qǐng)求信息等參數(shù)。

所以接下來我們看

this.getAuthenticationManager().authenticate(authRequest);

這里有一個(gè)AuthenticationManager,但是真正調(diào)用的是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判斷是否支持當(dāng)前傳入進(jìn)來的Authentication,目前我們使用的是UsernamePasswordAuthenticationToken,因?yàn)槌藥ぬ?hào)密碼登錄的方式,還會(huì)有其他的方式,比如SocialAuthenticationToken。
  2. 根據(jù)我們目前所使用的UsernamePasswordAuthenticationToken,provider對(duì)應(yīng)的是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.用戶信息預(yù)檢查
    this.preAuthenticationChecks.check(user);
    // 3.附加的檢查(密碼檢查)
    this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
  } catch (AuthenticationException var7) {    
  }
  // 4.最后的檢查
  this.postAuthenticationChecks.check(user);
  // 5.返回真正的經(jīng)過認(rèn)證的Authentication 
  return this.createSuccessAuthentication(principalToReturn, authentication, user);
}
  1. 去調(diào)用自己實(shí)現(xiàn)的UserDetailsService,返回UserDetails
  2. 對(duì)UserDetails的信息進(jìn)行校驗(yàn),主要是帳號(hào)是否被凍結(jié),是否過期等
  3. 對(duì)密碼進(jìn)行檢查,這里調(diào)用了PasswordEncoder
  4. 檢查UserDetails是否可用。
  5. 返回經(jīng)過認(rèn)證的Authentication

這里的兩次對(duì)UserDetails的檢查,主要就是通過它的四個(gè)返回boolean類型的方法。
經(jīng)過信息的校驗(yàn)之后,通過UsernamePasswordAuthenticationToken的構(gòu)造方法,返回了一個(gè)經(jīng)過認(rèn)證的Authentication。

拿到經(jīng)過認(rèn)證的Authentication之后,會(huì)再去調(diào)用successHandler。或者未通過認(rèn)證,去調(diào)用failureHandler。

認(rèn)證結(jié)果如何在多個(gè)請(qǐng)求之間共享

再完成了用戶認(rèn)證處理流程之后,我們思考一下是如何在多個(gè)請(qǐng)求之間共享這個(gè)認(rèn)證結(jié)果的呢?

因?yàn)闆]有做關(guān)于這方面的配置,所以可以聯(lián)想到默認(rèn)的方式應(yīng)該是在session中存入了認(rèn)證結(jié)果。

那么是什么時(shí)候存放入session中的呢?

我們可以接著認(rèn)證流程的源碼往后看,在通過attemptAuthentication方法后,如果認(rèn)證成功,會(huì)調(diào)用successfulAuthentication,該方法中,不僅調(diào)用了successHandler,還有一行比較重要的代碼

SecurityContextHolder.getContext().setAuthentication(authResult);

SecurityContextHolder是對(duì)于ThreadLocal的封裝。 ThreadLocal是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類,通過它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后,只有在指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù),對(duì)于其他線程來說則無法獲取到數(shù)據(jù)。 更多的關(guān)于ThreadLocal的原理可以看看我以前的文章。

一般來說同一個(gè)接口的請(qǐng)求和返回,都會(huì)是在一個(gè)線程中完成的。我們?cè)赟ecurityContextHolder中放入了authResult,再其他地方也可以取出來的。

最后就是在SecurityContextPersistenceFilter中取出了authResult,并存入了session

SecurityContextPersistenceFilter也是一個(gè)過濾器,它處于整個(gè)Security過濾器鏈的最前方,也就是說開始驗(yàn)證的時(shí)候是最先通過該過濾器,驗(yàn)證完成之后是最后通過。

獲取認(rèn)證用戶信息

/**
 * 獲取當(dāng)前登錄的用戶
 * @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;
}

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 一文帶你全面了解Java?Hashtable

    一文帶你全面了解Java?Hashtable

    HashTable是jdk?1.0中引入的產(chǎn)物,基本上現(xiàn)在很少使用了,但是會(huì)在面試中經(jīng)常被問到。本文就來帶大家一起深入了解一下Hashtable,需要的可以參考一下
    2022-09-09
  • SpringBoot整合SpringSecurity實(shí)現(xiàn)JWT認(rèn)證的項(xiàng)目實(shí)踐

    SpringBoot整合SpringSecurity實(shí)現(xiàn)JWT認(rèn)證的項(xiàng)目實(shí)踐

    本文會(huì)通過創(chuàng)建SpringBoot項(xiàng)目整合SpringSecurity,實(shí)現(xiàn)完整的JWT認(rèn)證機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • Java JDBC導(dǎo)致的反序列化攻擊原理解析

    Java JDBC導(dǎo)致的反序列化攻擊原理解析

    這篇文章主要介紹了Java JDBC導(dǎo)致的反序列化攻擊原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Springboot 使用 JSR 303 對(duì) Controller 控制層校驗(yàn)及 Service 服務(wù)層 AOP 校驗(yàn) 使用消息資源文件對(duì)消息國際化

    Springboot 使用 JSR 303 對(duì) Controller 控制層校驗(yàn)及 Service 服務(wù)層 AOP 校驗(yàn)

    這篇文章主要介紹了Springboot 使用 JSR 303 對(duì) Controller 控制層校驗(yàn)及 Service 服務(wù)層 AOP 校驗(yàn) 使用消息資源文件對(duì)消息國際化的相關(guān)知識(shí),需要的朋友可以參考下
    2017-12-12
  • SpringBoot接收前端參數(shù)的幾種方式分享

    SpringBoot接收前端參數(shù)的幾種方式分享

    這篇文章給大家分享幾種SpringBoot接收前端參數(shù)的方式,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2023-09-09
  • idea使用spring Initializr 快速搭建springboot項(xiàng)目遇到的坑

    idea使用spring Initializr 快速搭建springboot項(xiàng)目遇到的坑

    這篇文章主要介紹了idea使用spring Initializr 快速搭建springboot項(xiàng)目遇到的坑,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • spring data jpa分頁查詢示例代碼

    spring data jpa分頁查詢示例代碼

    本篇文章主要介紹了spring data jpa分頁查詢示例代碼,分頁在很多項(xiàng)目中都能使用,具有一定的參考價(jià)值,有興趣的可以了解一下。
    2017-03-03
  • MyBatis入門初體驗(yàn)之使用大全(2)

    MyBatis入門初體驗(yàn)之使用大全(2)

    這篇文章主要介紹了MyBatis入門初體驗(yàn)之使用大全(2)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-07-07
  • spring boot 監(jiān)聽容器啟動(dòng)代碼實(shí)例

    spring boot 監(jiān)聽容器啟動(dòng)代碼實(shí)例

    這篇文章主要介紹了spring boot 監(jiān)聽容器啟動(dòng)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java使用Semaphore對(duì)單接口進(jìn)行限流

    Java使用Semaphore對(duì)單接口進(jìn)行限流

    本篇主要講如何使用Semaphore對(duì)單接口進(jìn)行限流,主要有三種方式,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-07-07

最新評(píng)論