Spring Security 實戰(zhàn)干貨:客戶端OAuth2授權(quán)請求的入口

1. 前言

Spring Security 實戰(zhàn)干貨:OAuth2第三方授權(quán)初體驗一文中我先對OAuth2.0涉及的一些常用概念進行介紹,然后直接通過一個DEMO來讓大家切身感受了OAuth2.0第三方授權(quán)功能。今天我們來一步一步分析這其中的機制。

2. 抓住源頭

http://localhost:8082/oauth2/authorization/gitee

上面這個請求URL是我們在上一篇文章中提到的客戶端進行第三方認證操作的起點,默認格式為{baseUrl}/oauth2/authorization/{clientRegistrationId},其中clientRegistrationId代表著一個第三方標(biāo)識,可以是微信、支付寶等開放平臺,這里為gitee。用戶點擊了這個請求后就開始了授權(quán)之旅。假如大家都是從零開始的小白,肯定是要從這個入口來一步一步探尋其中的機制的。Spring Security一定是攔截到了/oauth2/authorization后才啟用了OAuth2相關(guān)的處理邏輯。那就去抓住這個源頭!從源碼中搜索嘛!IDEA 快捷鍵CTRL SHIFT R 就可以全局搜索結(jié)果了。

IDEA 搜索結(jié)果

不出所料找到了三個地方,記下來一個一個看!

OAuth2AuthorizationRequestRedirectWebFilter

先來看第一個OAuth2AuthorizationRequestRedirectWebFilter,它實現(xiàn)了Spring Webflux的WebFilter接口,這顯然是Webflux的東西,如果你用到Webflux的話這個會有用,但是不是現(xiàn)在我們用的。

DefaultOAuth2AuthorizationRequestResolver

第二個是干嘛的呢,從名稱上看著是一個默認OAuth2授權(quán)請求解析器。有時候名稱起的好就知道這個東西大致上干嘛的,不得不說優(yōu)秀框架細節(jié)抓的很好。它實現(xiàn)了接口OAuth2AuthorizationRequestResolver:

public interface OAuth2AuthorizationRequestResolver {

   /**
    * 從HttpServletRequest對象中解析封裝 OAuth2AuthorizationRequest
    */
   OAuth2AuthorizationRequest resolve(HttpServletRequest request);

   /**
    * 從HttpServletRequest對象以及clientRegistrationId中解析封裝 OAuth2AuthorizationRequest
    */
   OAuth2AuthorizationRequest resolve(HttpServletRequest request, String clientRegistrationId);

}

也就是說當(dāng)我們請求/oauth2/authorization時,DefaultOAuth2AuthorizationRequestResolver 會從/oauth2/authorization對應(yīng)的HttpServletRequest中提取數(shù)據(jù)封裝到OAuth2AuthorizationRequest請求對象中做進一步使用。

注意/oauth2/authorization這個默認攔截標(biāo)識也是可以自定義的。

OAuth2AuthorizationRequest

這里簡單提一下OAuth2AuthorizationRequest 封裝了我們上一文所描述的一些OAuth2相關(guān)概念參數(shù),后續(xù)這個請求類我們會用到它。

public final class OAuth2AuthorizationRequest implements Serializable {
    private static final long serialVersionUID = 520L;
    private String authorizationUri;
    private AuthorizationGrantType authorizationGrantType;
    private OAuth2AuthorizationResponseType responseType;
    private String clientId;
    private String redirectUri;
    private Set<String> scopes;
    private String state;
    private Map<String, Object> additionalParameters;
    private String authorizationRequestUri;
    private Map<String, Object> attributes;
    // 其它方法略
}

OAuth2AuthorizationRequestRedirectFilter

就剩下這個線索了,一看到它繼承了OncePerRequestFilter我就知道肯定是他了。甚至它的成員變量包含了用來解析OAuth2請求的OAuth2AuthorizationRequestResolver。到這里我們的路子就走對了,開始分析這個過濾器,下面是其核心過濾邏輯,這就是我們想要知道的OAuth2授權(quán)請求是如何被攔截處理的邏輯。

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
      throws ServletException, IOException {

   try {
      OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request);
      if (authorizationRequest != null) {
         this.sendRedirectForAuthorization(request, response, authorizationRequest);
         return;
      }
   } catch (Exception failed) {
      this.unsuccessfulRedirectForAuthorization(request, response, failed);
      return;
   }

   try {
      filterChain.doFilter(request, response);
   } catch (IOException ex) {
      throw ex;
   } catch (Exception ex) {
      // Check to see if we need to handle ClientAuthorizationRequiredException
      Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
      ClientAuthorizationRequiredException authzEx = (ClientAuthorizationRequiredException) this.throwableAnalyzer
         .getFirstThrowableOfType(ClientAuthorizationRequiredException.class, causeChain);
      if (authzEx != null) {
         try {
            OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestResolver.resolve(request, authzEx.getClientRegistrationId());
            if (authorizationRequest == null) {
               throw authzEx;
            }
            this.sendRedirectForAuthorization(request, response, authorizationRequest);
            this.requestCache.saveRequest(request, response);
         } catch (Exception failed) {
            this.unsuccessfulRedirectForAuthorization(request, response, failed);
         }
         return;
      }

      if (ex instanceof ServletException) {
         throw (ServletException) ex;
      } else if (ex instanceof RuntimeException) {
         throw (RuntimeException) ex;
      } else {
         throw new RuntimeException(ex);
      }
   }
}

doFilterInternal對應(yīng)的流程如下:

OAuth2AuthorizationRequestRedirectFilter執(zhí)行流程

根據(jù)這個流程,如果要搞清楚Spring Security OAuth2是如何重定向到第三方的話就要深入研究sendRedirectForAuthorization方法,基于篇幅原因我會在下一篇進行分析。

3. 總結(jié)

今天我們從源頭一步一步找到OAuth2授權(quán)的處理入口,并初步分析了幾個關(guān)鍵組件的作用以及核心攔截器的攔截邏輯。后續(xù)我們將層層深入循序漸進地搞清楚其運作流程,不要走開,鎖定:碼農(nóng)小胖哥 循序漸進學(xué)習(xí)Spring Security OAuth2 。

關(guān)注公眾號:碼農(nóng)小胖哥,獲取更多資訊

個人博客:https://felord.cn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容