天天看點

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

JWT 使用

前面簡單介紹了把預設的頁面登入改為前後端分離的接口異步登入的方法,可以幫我們實作基本的前後端分離登入功能。但是這種基本的登入和前面的頁面登入還有一個一樣的地方,就是使用session和cookie來維護登入狀态,這種方法的問題在于,擴充性不好。單機當然沒有問題,如果是伺服器叢集,或者是跨域的服務導向架構,就要求 session 資料共享,每台伺服器都能夠讀取 session。

一種解決方案是 session 資料持久化,寫入redis或别的持久層。各種服務收到請求後,都向持久層請求資料。這種方案的優點是架構清晰,缺點是工程量比較大。另外,持久層萬一挂了,就會單點失敗。

另一種方案是伺服器索性不儲存 session 資料了,所有資料都儲存在用戶端,每次請求都發回伺服器。JWT 就是這種方案的一個代表。關于JWT的理論知識,建議參考 阮一峰 大神寫的教程 :http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html,這是我認為可能是寫的最清晰的一個,下面的jwt的實作也是根據此教程來實作。

具體的理論知識可以參考教程,這裡簡單說下流程,使用者登入成功後,在header中傳回使用者一個token資訊,這個資訊裡面包含了加密的使用者資訊和數字簽名,最重要的還有過期時間,用戶端接到後,每次通路接口header中都帶着這個token,服務端驗證成功後就表示處于登入狀态,過期後再從新擷取即可。

具體的token内容包含了頭部(加密資訊),載體(使用者資訊),簽名(簽名兩個部分的前面)三大塊,三大塊之間用英文句号(也就是 ".")連接配接起來,組成一個完整的token資訊

流程設計

根據前面的理論知識,我們來設計一下如何使用jwt。首先我們使用jwt,就可以不再使用session和cookie,是以第一步就是:

  1. 在security配置檔案中配置session為無狀态。

然後考慮建構jwt消息體,有三個部分,第一個部分就是頭部,内容是加密類型:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

上面代碼中,alg屬性表示簽名的算法(algorithm),預設是 HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的類型(type),JWT 令牌統一寫為JWT,最後,将上面的 JSON 對象使用 Base64URL 算法轉成字元串,作為第一部分。是以第二步就是:

  1. 在security配置檔案中配置session為無狀态。
  2. 确定header資訊格式

下一步确定第二部分,消息載體(Payload),這也是一個json對象,用來存放實際需要傳遞的資料。JWT 規定了7個官方字段,供選用:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

當然除了這些還可以加一些其它内容,比如使用者資訊,這個 JSON 對象也要使用 Base64URL 算法轉成字元串,是以第三步和第四步就是:

  1. 在security配置檔案中配置session為無狀态。
  2. 确定header資訊格式
  3. 确定消息體
  4. 使用 HMAC SHA256 算法 對header和消息體進行簽名作為第三部分

現在token的消息基本組合完成了,使用者登入成功和用戶端通路接口,都要把token放在header裡面,名字是 Authorization 。是以最後一步就是,用戶端正常通路非登入等接口時,驗證token的合法性,是以,總體設計流程如下:

  1. 在security配置檔案中配置session為無狀态。
  2. 确定header資訊格式
  3. 确定消息體
  4. 使用 HMAC SHA256 算法 對header和消息體進行簽名作為第三部分
  5. 添加過濾器,驗證token合法性

修改配置類

上面的流程設計完了,下面我們按照流程修改項目,首先修改security配置類:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

配置完後,啟動項目,通路登入,登入成功後可以看到,沒有任何cookie儲存下來。

定義JWT工具類

首先來定義幾個常量:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

然後定義Base64URL 算法編碼和解碼方法:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

然後定義HmacSHA256 加密算法和擷取簽名的方法:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

最後來設計一個簡單驗證token的方法:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

這樣jwt工具類就設計好了,目前這幾個方法足夠操作token内容。

定義JWT消息對象

下面來定義jwt的内容,其實内容很簡單,就三個部分,是以,定義三個字段即可:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

來看一下構造方法,

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

這個構造方法很便捷,使用它建立對象以後,jwt的三個部分基本都完成了,header部分和payload部分都編碼了,簽名也完成了,是以下面重寫toString方法直接可以生成token:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

從這裡可以看出,token整體預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用密鑰再加密一次。是以不要把密碼等重要資訊放入token。

修改登入成功處理器

使用者登入成功後,不再把session發給使用者,而是把jwt發送給使用者,是以修改登入成功處理器如下:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

注意上面手動把使用者的密碼資訊設定為null。這裡為了友善,直接使用fastjson組合對象。

修改實體類

帶着token通路接口的時候,需要把token轉回登入使用者對象,是以我們的使用者實體類和token中帶的字段名字一緻,來修改一下,先看角色實體類:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

再看使用者實體類:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

可以看到,基本的原則就是修改的名字和父類的必要字段名字一緻就行,這也是建議的字段名字。

編寫token驗證過濾器

我們把security的session改為無狀态後,雖然不再傳遞session,但是security的過濾器并沒有失效,是以造成的效果就是登入成功後,通路接口顯示未登入。現在我們使用token就要在登入前加一個驗證token的過濾器,驗證通過後直接把資訊放到SecurityContextHolder中。這樣每次登入靠驗證token來判斷是否登入,不再靠session。來看這個過濾器:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

這個過濾器很簡單,繼承了 GenericFilterBean 類,直接擷取token,判斷token不為空,驗證token,并從token的payload中取出使用者資訊,放入SecurityContextHolder中,驗證失敗或者token過期直接傳回token錯誤。邏輯很簡單。

最後在security類中,把這個過濾器配置到前面:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

這樣我們自定義的jwt流程就完成了。可以在postman中測試一下,首先是登入:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

登入成功後,可以看到header中放着token的資訊,然後使用token放入另一個接口的header中通路接口,可以看到通路成功:

jwt判斷token是否過期_SpringBootSecurity學習(13)前後端分離版之JWTJWT 使用流程設計修改配置類定義JWT工具類定義JWT消息對象修改登入成功處理器修改實體類編寫token驗證過濾器JWT的幾個特點

有興趣的可以debug跟蹤一下流程。

JWT的幾個特點

  • (1)JWT 預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用密鑰再加密一次。
  • (2)JWT 不加密的情況下,不能将秘密資料寫入 JWT。
  • (3)JWT 不僅可以用于認證,也可以用于交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。
  • (4)JWT 的最大缺點是,由于伺服器不儲存 session 狀态,是以無法在使用過程中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。
  • (5)JWT 本身包含了認證資訊,一旦洩露,任何人都可以獲得該令牌的所有權限。為了減少盜用,JWT 的有效期應該設定得比較短。對于一些比較重要的權限,使用時應該再次對使用者進行認證。
  • (6)為了減少盜用,JWT 不應該使用 HTTP 協定明碼傳輸,要使用 HTTPS 協定傳輸。

代碼位址:https://gitee.com/blueses/spring-boot-security 14

繼續閱讀