一、認識JWT(Json Web Token)
JSON Web Token(縮寫 JWT)是目前最流行的跨域認證解決方案,一般來說如果是使用session或者cookie來實現,但是這兩者都有一定的局限性。而我們使用JWT去實現跨域認證的話,就可以將JWT放在請求頭里面(也有人會把它放在post請求的數據體里,但是這種方法比較少見)。
1.JWT的數據結構
JWT解析出來之后其實就是一個Json,但是在傳送的過程中,它是一串很長的字符串,中間用“.”分割成三個部分
- Header(頭部)
- Payload(負載)
- Signature(簽名)
寫成一行就是
Header.Payload.Signature
舉一個我在真正開發時遇到的一個JWT,里面的id_token就是一個JWT,可以看到它就是用兩個“.”去分割開的。

2.Header(頭部)
Header 部分是一個 JSON 對象,描述 JWT 的元數據。
{ "alg": "HS256", "typ": "JWT" }
上面代碼中,alg屬性表示簽名的算法(algorithm),默認是 HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的類型(type),JWT 令牌統一寫為JWT。
最后,將上面的 JSON 對象使用 Base64URL 算法成字符串。說到這個,如果你的簽名算法是別的算法的話,去解析JWT的時候會報錯
Key bytes can only be specified for HMAC signatures. Please specify a PublicKey or PrivateKey instance
這時候你要去檢查一下你的密鑰是否正確,我當時的情況就是公鑰搞錯了,不過我看網上也有人說什么可以設置一下算法的類型,但是看不懂是怎么操作的,全網就搜到了那個方法,全都是一模一樣,復制來復制去的,很沒意思。
3.Payload(負載)
Payload是JWT最重要的數據,里面會攜帶很多信息,我們要實現登錄認證一般都在里面拿取到我們想要的數據。JWT 規定了7個官方字段供選用。
- iss (issuer):簽發人
- exp (expiration time):過期時間
- sub (subject):主題
- aud (audience):受眾
- nbf (Not Before):生效時間
- iat (Issued At):簽發時間
- jti (JWT ID):編號
除了官方字段,我們還可以在生成JWT的時候定義部分私有字段名,下面是我工作是遇到的JWT,他們就定義了自己的私有字段,將payload解析之后就是下面的圖片

4.Signature(簽名)
Signature 部分是對前兩部分的簽名,防止數據篡改。
首先,需要指定一個密鑰(secret)。這個密鑰只有服務器才知道,不能泄露給用戶。然后,使用 Header 里面指定的簽名算法(默認是 HMAC SHA256),按照下面的公式產生簽名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出簽名以后,把 Header、Payload、Signature 三個部分拼成一個字符串,每個部分之間用"點"(.)分隔,就可以返回給用戶。
5.JWT的使用
一般是放在請求頭里面,注意“Bearer”和“token”之間有一個空格。
Authorization: Bearer <token>
二、解析JWT
解析JWT網上有很多個jar可以拿來使用,我這里使用的jose4j來解析。
/** * 驗證Jwt並拿到email值 * * @param jwtTicket Jwt票據 * @param publicKeyStr 公鑰 */ public String validateAndGetToken(String jwtTicket, String publicKeyStr) throws Exception { JsonWebSignature jws = new JsonWebSignature(); jws.setCompactSerialization(jwtTicket); JsonWebKey jsonWebKey = JsonWebKey.Factory.newJwk(publicKeyStr); String keyId = jsonWebKey.getKeyId(); jws.setKey(jsonWebKey.getKey()); boolean verifySignature = jws.verifySignature(); if (!verifySignature) { throw new Exception("Invalid signature!"); } String payload = jws.getPayload(); JwtClaims claims = JwtClaims.parse(payload); NumericDate expirationTime = claims.getExpirationTime(); Map<String, Object> claimsMap = claims.getClaimsMap(); if (expirationTime == null) { logWarn("Invalid signature!"); return null; } if (!expirationTime.isAfter(NumericDate.now())) { logWarn("Jwt ticket expired!"); return null; } String username = (String) claimsMap.get("email"); return username; }
這里claimsMap.get("email")就是拿到郵箱地址,因為我做的是郵箱的單點登錄,所以這里是email,根據具體情況去獲取具體的字段就可以了。
參考鏈接
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
