OAuth2.0實戰!使用JWT令牌認證!

bucaichenmou發表於2021-12-07

大家好,我是不才陳某~

這是《Spring Security 進階》的第3篇文章,往期文章如下:

文章都是成體系的,陳某預設看到這篇文章的讀者都已經看了前期的文章,之前的知識點就不再詳細介紹了。

今天這篇文章介紹一下OAuth2.0如何整合JWT頒發令牌,這也是目前企業中主流的令牌形式。

文章目錄如下:

什麼是JWT?

OAuth2.0體系中令牌分為兩類,分別是透明令牌不透明令牌

不透明令牌則是令牌本身不儲存任何資訊,比如一串UUID,上篇文章中使用的InMemoryTokenStore就類似這種。

因此資源服務拿到這個令牌必須調呼叫認證授權服務的介面進行令牌的校驗,高併發的情況下延遲很高,效能很低,正如上篇文章中資源伺服器中配置的校驗,如下:

透明令牌本身就儲存這部分使用者資訊,比如JWT,資源服務可以呼叫自身的服務對該令牌進行校驗解析,不必呼叫認證服務的介面去校驗令牌。

JWT相信大家都有了解,分為三部分,分別是頭部載荷簽名,如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTYzODYwNTcxOCwiYXV0aG9yaXRpZXMiOlsiUk9MRV91c2VyIl0sImp0aSI6ImRkNTVkMjEzLThkMDYtNGY4MC1iMGRmLTdkN2E0YWE2MmZlOSIsImNsaWVudF9pZCI6Im15anN6bCJ9.
koup5-wzGfcSVnaaNfILwAgw2VaTLvRgq2JVnIHYe_Q

頭部定義了JWT基本資訊,如型別和簽名演算法。

載荷包含了一些基本資訊(簽發時間、過期時間.....),另外還可以新增一些自定義的資訊,比如使用者的部分資訊。

簽名部分將前兩個字串用 . 連線後,使用頭部定義的加密演算法,利用金鑰進行簽名,並將簽名資訊附在最後。

OAuth2.0認證授權服務搭建

OAuth2.0分為認證授權中心、資源服務,認證中心用於頒發令牌,資源服務解析令牌並且提供資源。

1、案例架構

新建oauth2-auth-server-jwt模組,沿用上篇文章妹子始終沒搞懂OAuth2.0,今天整合Spring Cloud Security 一次說明白!的程式碼,在其之上做些修改,目錄如下:

2、令牌配置

令牌相關的配置都放在了AccessTokenConfig這個配置類中,程式碼如下:

1、 JwtAccessTokenConverter

令牌增強類,用於JWT令牌和OAuth身份進行轉換

2、TokenStore

令牌的儲存策略,這裡使用的是JwtTokenStore,使用JWT的令牌生成方式,其實還有以下兩個比較常用的方式:

  • RedisTokenStore:將令牌儲存到Redis中,此種方式相對於記憶體方式來說效能更好
  • JdbcTokenStore:將令牌儲存到資料庫中,需要新建從對應的表,有興趣的可以嘗試

3、SIGN_KEY

JWT簽名的祕鑰,這裡使用的是對稱加密,資源服務中也要使用相同的祕鑰進行校驗和解析JWT令牌

注意:實際工作中還是要使用非對稱加密的方式,比較安全,這種方式後續文章介紹。

3、令牌管理服務的配置

這個放在了AuthorizationServerConfig這個配置類中,程式碼如下:

使用的是DefaultTokenServices這個實現類,其中可以配置令牌相關的內容,比如access_tokenrefresh_token的過期時間,預設時間分別為12小時、30天。

最重要的一行程式碼當然是設定令牌增強,使用JWT方式生產令牌,如下:

services.setTokenEnhancer(jwtAccessTokenConverter);

4、令牌訪問端點新增tokenServices

AuthorizationServerEndpointsConfigurer中新增這個令牌服務,程式碼如下:

好了,至此認證中心的JWT令牌生成方式配置完成了.........

案例原始碼已經上傳GitHub,關注公號:碼猿技術專欄,回覆關鍵詞 9529 獲取!

OAuth2.0資源服務搭建

資源服務搭建非常簡單了,配置一個JWT令牌校驗服務即可。

1、案例架構

新建一個oauth2-auth-resource-jwt模組,目錄如下:

2、令牌配置

直接複用授權服務的AccessTokenConfig,由於資源服務需要校驗解析JWT令牌,因此直接複用即可,程式碼如下:

注意:這裡的JWT加密的祕鑰一定要和認證中心的一樣。

3、配置令牌服務

生成的ResourceServerTokenServices物件,其中使用JWT令牌增強,如下:

4、資源ID和令牌校驗服務配置

資源id令牌服務配置到ResourceServerSecurityConfigurer中,程式碼如下:

由於使用了JWT這種透明令牌,令牌本身攜帶著部分使用者資訊,因此不需要通過遠端呼叫認證中心的介面校驗令牌。

案例原始碼已經上傳GitHub,關注公號:碼猿技術專欄,回覆關鍵詞 9529 獲取!

測試

下面通過獲取令牌、呼叫資源進行測試邏輯是否走通。

1、使用密碼模式獲取令牌

POSTMAN請求如下:

可以看到已經成功返回了JWT令牌。

2、攜帶令牌呼叫資源服務

直接拿著獲取的access_token呼叫資源服務的介面,請求如下:

好了,JWT令牌測試成功............

原始碼追蹤

原始碼中最重要的部分當然是獲取令牌、校驗令牌這兩個流程了,小陳某下面詳細說說。

1、獲取令牌

獲取令牌就比較簡簡單了,當然從介面 /oauth/token入手了,這個介面在TokenEndpoint#postAccessToken()方法中,如下圖:

這個方法中有兩個關鍵步驟,如下:

1、根據clientId載入客戶端資訊

這一步是從根據客戶端傳入的clientId獲取客戶端的詳細資訊,程式碼如下:

ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

這裡的ClientDetailsService有兩類,如下:

  • InMemoryClientDetailsService:客戶端配置儲存在記憶體中,本篇文章所使用的便是這個
  • JdbcClientDetailsService:客戶端配置儲存在資料庫中,後續文章介紹

2、生成OAuth2AccessToken返回客戶端

OAuth2AccessToken是封裝的返回物件,/oauth/token這個介面的作用就是將令牌封裝到OAuth2AccessToken返回,程式碼如下:

OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

getTokenGranter():獲取授權型別,比如密碼型別、授權碼型別

grant():這個方法則是真正的業務方法,其中呼叫DefaultTokenServices#createAccessToken() 方法生成令牌。

DefaultTokenServices這個還記得嗎,令牌服務,在AuthorizationServerConfig配置檔案配置的,如下:

createAccessToken()方法內部真正的業務方法其實是JwtAccessTokenConverter#enhance(),內部生成JWT令牌,封裝進入OAuth2AccessToken物件返回,方法如下:

JwtAccessTokenConverter這個還記得嗎?令牌增強類,在AccessTokenConfig這個配置檔案中配置的,如下:

主流程圖如下:

2、校驗令牌

校驗令牌的更加簡單了,入口就在OAuth2AuthenticationProcessingFilter這個過濾器,內部會呼叫OAuth2AuthenticationManager中的authenticate()方法進行驗證令牌。

校驗主流程如下:

自己debug跟著原始碼試試吧............

案例原始碼已經上傳GitHub,關注公號:碼猿技術專欄,回覆關鍵詞 9529 獲取!

最後說一句(別白嫖,求關注)

陳某每一篇文章都是精心輸出,已經寫了3個專欄,整理成PDF,獲取方式如下:

  1. 《Spring Cloud 進階》PDF:關注公號:【碼猿技術專欄】回覆關鍵詞 Spring Cloud 進階 獲取!
  2. 《Spring Boot 進階》PDF:關注公號:【碼猿技術專欄】回覆關鍵詞 Spring Boot進階 獲取!
  3. 《Mybatis 進階》PDF:關注公號:【碼猿技術專欄】回覆關鍵詞 Mybatis 進階 獲取!

如果這篇文章對你有所幫助,或者有所啟發的話,幫忙點贊在看轉發收藏,你的支援就是我堅持下去的最大動力!

相關文章