原文地址:JWT 基礎教程
博客地址:http://www.extlight.comhtml
針對先後端分離的項目,大可能是經過 token 進行身份認證來進行交互,今天將介紹一種簡單的建立 token 的方式 -- JWT。前端
JSON Web Token(JWT)是一個很是輕巧的規範。這個規範容許咱們使用 JWT 在用戶和服務器之間傳遞安全可靠的信息。java
一個 JWT 實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名。前兩部分須要通過 Base64 編碼,後一部分經過前兩部分 Base64 編碼後再加密而成。git
若是讀者不理解上邊的陳述,沒關係,下文會詳細講解。github
頭部(Header)web
頭部用於描述關於該 JWT 的最基本的信息,例如其類型以及簽名所用的算法等,也能夠被表示成一個 JSON 對象。例如:算法
{"typ":"JWT","alg":"HS256"}
在頭部指明瞭簽名算法是 HS256 算法。編程
通過 Base64 編碼獲得:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9(第一部分)。下文參考資料提供線上加密/解密網址,感興趣的讀者能夠本身嘗試。json
載荷(playload)後端
載荷就是存放有效信息的地方。這些有效信息包含三個部分:
(1)標準中註冊的聲明(建議但不強制使用)
iss: jwt簽發者 sub: jwt所面向的用戶 aud: 接收jwt的一方 exp: jwt的過時時間,這個過時時間必需要大於簽發時間 nbf: 定義在什麼時間以前,該jwt都是不可用的. iat: jwt的簽發時間 jti: jwt的惟一身份標識,主要用來做爲一次性token,從而回避重放攻擊
(2)公共的聲明
公共的聲明能夠添加任何的信息,通常添加用戶的相關信息或其餘業務須要的必要信息,但不建議添加敏感信息,由於該部分在客戶端可解密。
例如:
{"id":"123456","name":"MoonlightL","sex":"male"}
將該 json 字符串進行 Base64 編碼獲得:eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9(第二部分)。
(3)私有的聲明
私有聲明是提供者和消費者所共同定義的聲明,通常不建議存放敏感信息,由於base64 是對稱解密的,意味着該部分信息能夠歸類爲明文信息。
注意:載荷中的這3個聲明並非都要同時設置。
簽證(signature)
jwt的第三部分是一個簽證信息。
這個部分須要 Base64 加密後的 header 和 Base64 加密後的 payload 使用 「.」 鏈接組成的字符串,而後經過 header 中聲明的加密方式進行加鹽 secret 組合加密,而後就構成了 jwt 的第三部分。
即將 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9 進行 HS256 算法加密(header 定義的)獲得:
e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f(第三部分)。
最後,咱們將上述的 3 個部分的字符串經過 「.」 進行拼接獲得 JWT:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f 。
爲了驗證上述生成的 jwt 是否合法,咱們能夠登陸 JWT 官網,官網界面提供 JWT 解密功能,將生成好的 JWT 複製到以下圖中進行解密:
在實際開發中,用戶登陸成功後,後端生成 jwt 返回給前端,以後,前端與後端交互時攜帶 jwt 讓後端驗證 jwt 的合法性。
經過上述的介紹,咱們已經瞭解到什麼是 JWT 以及 JWT 生成的規則,如今咱們經過代碼方式來生成 JWT。
JWT 官網提供了經過不一樣編程語言來建立 JWT 的工具類/庫,這次測試咱們選用 JJWT 。
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.10.5</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.10.5</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.10.5</version> <scope>runtime</scope> </dependency>
import java.security.Key; import java.util.Date; import java.util.UUID; import org.junit.Test; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.JwtException; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; public class JWTTest { @Test public void testJWT() { Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); System.out.println("=============建立 JWT==========="); Date now = new Date(); JwtBuilder builder= Jwts.builder() .setId(UUID.randomUUID().toString()) // 載荷-標準中註冊的聲明 .setSubject("admin") // 載荷-標準中註冊的聲明 .setIssuedAt(now) // 載荷-標準中註冊的聲明,表示簽發時間 .claim("id", "123456") // 載荷-公共的聲明 .claim("name", "MoonlightL") // 載荷-公共的聲明 .claim("sex", "male") // 載荷-公共的聲明 .signWith(key); // 簽證 String jwt = builder.compact(); System.out.println("生成的 jwt :" +jwt); System.out.println("=============解析 JWT==========="); try { Jws<Claims> result = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt); // 如下步驟隨實際狀況而定,只要上一行代碼執行不拋異常就證實 jwt 是有效的、合法的 Claims body = result.getBody(); System.out.println("載荷-標準中註冊的聲明 id:" + body.getId()); System.out.println("載荷-標準中註冊的聲明 subject:" + body.getSubject()); System.out.println("載荷-標準中註冊的聲明 issueAt:" + body.getIssuedAt()); System.out.println("載荷-公共的聲明的 id:" + result.getBody().get("id")); System.out.println("載荷-公共的聲明的 name:" + result.getBody().get("name")); System.out.println("載荷-公共的聲明的 sex:" + result.getBody().get("sex")); } catch (JwtException ex) { // jwt 不合法或過時都會拋異常 ex.printStackTrace(); } } }
執行結果:
=============建立 JWT=========== 生成的 jwt :eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI3ZjZmZjRlMC04YjM5LTQyYjUtOGRkNS0xN2M4ZjM5ZmZhNzMiLCJzdWIiOiJhZG1pbiIsImlhdCI6MTU0MzIwNTI4OSwiZXhwIjoxNTQzMjA1MzQ5LCJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.BtEi-GCj5mCunXD_g0Cra7CSE_bMxhTzlOELWKc17I8 =============解析 JWT=========== 載荷-標準中註冊的聲明 id:7f6ff4e0-8b39-42b5-8dd5-17c8f39ffa73 載荷-標準中註冊的聲明 subject:admin 載荷-標準中註冊的聲明 issueAt:Mon Nov 26 12:08:09 CST 2018 載荷-公共的聲明的 id:123456 載荷-公共的聲明的 name:MoonlightL 載荷-公共的聲明的 sex:male
注意:加密和解密 JWT 必須是同一個 Key 對象
注意:解密 JWT 時,必需要抓取 JwtException 異常,只要抓取到該異常說明該 JWT 不可用了
JJWT 庫還有其餘使用方式,具體資料請查看下邊提供的參考資料。