1. 程式人生 > >Spring Security 實戰乾貨:OAuth2授權請求是如何構建並執行的

Spring Security 實戰乾貨:OAuth2授權請求是如何構建並執行的

![](https://img2020.cnblogs.com/other/1739473/202011/1739473-20201111090740161-2099390223.png) 在[Spring Security 實戰乾貨:客戶端OAuth2授權請求的入口](https://mp.weixin.qq.com/s/XIawCpsULgeh3CWR3D7LYQ)中我們找到了攔截OAuth2授權請求入口`/oauth2/authorization`的過濾器`OAuth2AuthorizationRequestRedirectFilter`,並找到了真正發起OAuth2授權請求的方法`sendRedirectForAuthorization`。但是這個方法並沒有細說,所以今天接著上一篇把這個坑給補上。 ## 2. sendRedirectForAuthorization 這個`sendRedirectForAuthorization`方法沒多少程式碼,它的主要作用就是向第三方平臺進行授權重定向訪問。它所有的邏輯都和`OAuth2AuthorizationRequest`有關,因此我們對`OAuth2AuthorizationRequest`進行輕描淡寫是不行的,我們必須掌握`OAuth2AuthorizationRequest`是怎麼來的,幹嘛用的。 ### OAuth2AuthorizationRequestResolver 這就需要去分析解析類`OAuth2AuthorizationRequestResolver`,其核心方法有兩個過載,這裡分析一個就夠了。 ```java @Override public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { // registrationId是通過uri路徑引數/oauth2/authorization/{registrationId}獲得的 String registrationId = this.resolveRegistrationId(request); // 然後去請求物件request中提取key為action的引數,預設值是login String redirectUriAction = getAction(request, "login"); // 然後進入根本的解析方法 return resolve(request, registrationId, redirectUriAction); } ``` 上面方法裡面的`resolve(request, registrationId, redirectUriAction)`方法才是最終從`/oauth2/authorization`提取`OAuth2AuthorizationRequest`的根本方法。程式碼太多但是我儘量通俗易懂的來進行圖解。`resolve`方法會根據不同的授權方式(`AuthorizationGrantType`)來組裝不同的`OAuth2AuthorizationRequest`。 ## 3. OAuth2AuthorizationRequest 接下來就是OAuth2.0協議的核心重中之重了,可能以後你定製化的參考就來自這裡,這是圈起來要考的知識點。我會對`OAuth2AuthorizationRequestResolver`在各種授權方式下的`OAuth2AuthorizationRequest`物件的解析進行一個完全的總結歸納。大致分為以下兩部分: ### 3.1 由AuthorizationGrantType決定的 在不同`AuthorizationGrantType`下對`OAuth2AuthorizationRequest`的梳理。涉及到的成員變數有: - `authorizationGrantType` ,來自配置`spring.security.client.registration.{registrationId}.authorizationGrantType`。 - `responseType` , 由`authorizationGrantType` 的值決定,參考下面的JSON。 - `additionalParameters`,當`authorizationGrantType`值為`authorization_code`時需要額外的一些引數,參考下面JSON 。 - `attributes`,不同的`authorizationGrantType`存在不同的屬性。 > 其中類似`{registrationId}` 的形式表示` {registrationId}`是一個變數,例如 `registrationId=gitee`。 在OAuth2客戶端配置`spring.security.client.registration.{registrationId}`的字首中有以下五種情況。 當 `scope` 不包含`openid`而且`client-authentication-method`不為`none`時上述四個引數: ```json { "authorizationGrantType": "authorization_code", "responseType": "code", "additionalParameters": {}, "attributes": { "registration_id": "{registrationId}" } } ``` 當 `scope` 包含`openid`而且`client-authentication-method`不為`none`時上述四個引數: ```json { "authorizationGrantType": "authorization_code", "responseType": "code", "additionalParameters": { "nonce": "{nonce}的Hash值" }, "attributes": { "registration_id": "{registrationId}", "nonce": "{nonce}" } } ``` 當 `scope`不包含`openid`而且`client-authentication-method`為`none`時上述四個引數: ```json { "authorizationGrantType": "authorization_code", "responseType": "code", "additionalParameters": { "code_challenge": "{codeVerifier}的Hash值", // code_challenge_method 當不是SHA256可能沒有該key "code_challenge_method": "S256(如果是SHA256演算法的話)" }, "attributes": { "registration_id": "{registrationId}", "code_verifier": "Base64生成的安全{codeVerifier}" } } ``` 當 `scope`包含`openid`而且`client-authentication-method`為`none`時上述四個引數: ```json { "authorizationGrantType": "authorization_code", "responseType": "code", "additionalParameters": { "code_challenge": "{codeVerifier}的Hash值", // code_challenge_method 當不是SHA256可能沒有該key "code_challenge_method": "S256(如果是SHA256演算法的話)", "nonce": "{nonce}的Hash值" }, "attributes": { "registration_id": "{registrationId}", "code_verifier": "Base64生成的安全{codeVerifier}", "nonce": "{nonce}" } } ``` `implicit`下要簡單的多: ```json { "authorizationGrantType": "implicit", "responseType": "token", "attributes": {} } ``` ### 3.2 固定規則部分 上面是各種不同`AuthorizationGrantType`下的`OAuth2AuthorizationRequest`的成員變數個性化取值策略, 還有幾個引數的規則是固定的: - `clientId` 來自於配置,是第三方平臺給予我們的唯一標識。 - `authorizationUri`來自於配置,用來構造向第三方發起的請求URL。 - `scopes` 來自於配置,是第三方平臺給我們授權劃定的作用域,可以理解為角色。 - `state` 自動生成的,為了防止csrf 攻擊。 - `authorizationRequestUri` 向第三方平臺發起授權請求的,可以直接通過`OAuth2AuthorizationRequest`的構建類來設定或者通過上面的`authorizationUri`等引數來生成,稍後會把構造機制分析一波。 - `redirectUri` 當`OAuth2AuthorizationRequest`被第三方平臺收到後,第三方平臺會回撥這個URI來對授權請求進行相應,稍後也會來分析其機制。 #### authorizationRequestUri的構建機制 如果不顯式提供`authorizationRequestUri`就會通過`OAuth2AuthorizationRequest`中的 - `responseType` - `clientId` - `scopes` - `state` - `redirectUri` - `additionalParameters` 按照下面的規則進行拼接成`authorizationUri`的引數串,引數串的`key`和`value`都要進行URI編碼。 ``` authorizationUri?response_type={responseType.getValue()}&client_id={clientId}&scope={scopes元素一個字元間隔}&state={state}&redirect_uri={redirectUri}&{additionalParameter展開進行同樣規則的KV引數串} ``` 然後`OAuth2AuthorizationRequestRedirectFilter`負責重定向到`authorizationRequestUri`向第三方請求授權。 #### redirectUri 第三方收到響應會呼叫`redirectUri`,回撥也是有一定預設規則的,它遵循`{baseUrl}/{action}/oauth2/code/{registrationId}`的路徑引數規則。 - `baseUrl` 是從我們`/oauth2/authorization`請求中提取的基礎請求路徑。 - `action`,有兩種預設值`login`、`authorize` ,當`/oauth2/authorization`請求中包含了`action`引數時會根據`action`的值進行填充。 - `registrationId` 這個就不用多說了。 ## 4. 總結 通過對`OAuth2AuthorizationRequest`請求物件的規則進行詳細分析,我們應該能大致的知道的過濾器`OAuth2AuthorizationRequestRedirectFilter`流程: 1. 通過客戶端配置構建`ClientRegistration`,後續可以進行持久化。 2. 攔截`/oauth2/authorization`請求並構造`OAuth2AuthorizationRequest`,然後重定向到`authorizationRequestUri`進行請求授權。 3. 第三方通過`redirect_uri`進行相應。 那麼Spring Security OAuth2如何對第三方的回撥相應進行處理呢?關注:`碼農小胖哥` 為你揭曉這個答案。 `關注公眾號:Felordcn 獲取更多資訊` [個人部落格:https://felord.cn](https://fe