最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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)行了簡(jiǎn)單的使用介紹,基本上都是對(duì)于接口的介紹以及功能的實(shí)現(xiàn)。 這一篇文章嘗試從源碼的角度來上對(duì)用戶認(rèn)證流程做一個(gè)簡(jiǎn)單的分析。
在具體分析之前,我們可以先看看SpringSecurity的大概原理:

SpringSecurity基本原理

其實(shí)比較簡(jiǎn)單,主要是通過一系列的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;
}

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

相關(guān)文章

  • Servlet 過濾器詳細(xì)介紹

    Servlet 過濾器詳細(xì)介紹

    這篇文章介紹了Servlet 過濾器,有需要的朋友可以參考一下
    2013-10-10
  • 基于controller使用map接收參數(shù)的注意事項(xiàng)

    基于controller使用map接收參數(shù)的注意事項(xiàng)

    這篇文章主要介紹了基于controller使用map接收參數(shù)的注意事項(xiàng),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java常見的轉(zhuǎn)義字符舉例詳解

    Java常見的轉(zhuǎn)義字符舉例詳解

    在java字符常量中,反斜杠(\)是一個(gè)特殊的字符,被稱為轉(zhuǎn)義字符,它的作用是用來轉(zhuǎn)義后面一個(gè)字符,這篇文章主要給大吉介紹了關(guān)于Java常見轉(zhuǎn)義字符的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • SpringBoot與spring security的結(jié)合的示例

    SpringBoot與spring security的結(jié)合的示例

    這篇文章主要介紹了SpringBoot與spring security的結(jié)合的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-03-03
  • Java中方法優(yōu)先調(diào)用可選參數(shù)還是固定參數(shù)

    Java中方法優(yōu)先調(diào)用可選參數(shù)還是固定參數(shù)

    這篇文章主要介紹了Java中方法優(yōu)先調(diào)用可選參數(shù)還是固定參數(shù),可選參數(shù)是?JDK?5?中新增的特性,也叫變長(zhǎng)參數(shù)或可變參數(shù),固定參數(shù)的概念恰好與可選參數(shù)相反,固定參數(shù)也就是普通的參,下文更多詳細(xì)內(nèi)容需要的小伙伴可以參考一下
    2022-05-05
  • JAVA?OOM內(nèi)存溢出問題深入解析

    JAVA?OOM內(nèi)存溢出問題深入解析

    這篇文章主要為大家介紹了JAVA?OOM內(nèi)存溢出問題深入解析,在生產(chǎn)環(huán)境搶修中,我們經(jīng)常會(huì)碰到應(yīng)用系統(tǒng)java內(nèi)存OOM的情況,這個(gè)問題非常常見,今天我們就這個(gè)問題來深入學(xué)習(xí)探討一下
    2023-10-10
  • 使用SpringBoot項(xiàng)目導(dǎo)入openfeign版本的問題

    使用SpringBoot項(xiàng)目導(dǎo)入openfeign版本的問題

    這篇文章主要介紹了使用SpringBoot項(xiàng)目導(dǎo)入openfeign版本的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java實(shí)現(xiàn)的AES256加密解密功能示例

    Java實(shí)現(xiàn)的AES256加密解密功能示例

    這篇文章主要介紹了Java實(shí)現(xiàn)的AES256加密解密功能,結(jié)合完整實(shí)例形式分析了Java實(shí)現(xiàn)AES256加密解密功能的步驟與相關(guān)操作技巧,需要的朋友可以參考下
    2017-02-02
  • Java多數(shù)據(jù)源的三種實(shí)現(xiàn)方式小結(jié)

    Java多數(shù)據(jù)源的三種實(shí)現(xiàn)方式小結(jié)

    多數(shù)據(jù)源是在一個(gè)應(yīng)用程序中配置和使用多個(gè)不同的數(shù)據(jù)庫(kù)連接,本文主要介紹了Java多數(shù)據(jù)源的三種實(shí)現(xiàn)方式小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-03-03
  • RocketMQ獲取指定消息的實(shí)現(xiàn)方法(源碼)

    RocketMQ獲取指定消息的實(shí)現(xiàn)方法(源碼)

    這篇文章主要給大家介紹了關(guān)于RocketMQ獲取指定消息的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用RocketMQ具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08

最新評(píng)論

徐水县| 滨海县| 精河县| 岱山县| 阿巴嘎旗| 肇庆市| 清水河县| 奉新县| 杭锦旗| 库伦旗| 宣威市| 龙山县| 乌兰县| 黑水县| 朝阳县| 东山县| 泗水县| 武夷山市| 武义县| 邯郸市| 外汇| 天门市| 曲水县| 龙江县| 新昌县| 清流县| 台州市| 赣州市| 新建县| 屯留县| 舞钢市| 西盟| 五寨县| 嵊州市| 烟台市| 哈巴河县| 长岭县| 泉州市| 保靖县| 孟村| 杨浦区|