1. 概述
一個 JSON Web Token (JWT) 常被用於 REST API 安全中。 儘管令牌可以被 Spring Security OAuth 等框架解析,但我們可能希望在自己的代碼中處理令牌。
在本教程中,我們將 解碼並驗證 JWT 的完整性。
2. JWT 結構
首先,讓我們理解 JWT 的結構:
- header
- payload (通常被稱為 body)
- signature
簽名是可選的。一個有效的 JWT 可以只包含 header 和 payload 部分。但是,我們使用 signature 部分來驗證 header 和 payload 的內容以進行安全授權。
部分以 base64url 編碼的字符串表示,用 period (‘.’) 分隔符分隔。按照設計,任何人都可以解碼 JWT 並讀取 header 和 payload 部分的內容。但是,我們需要訪問用於創建簽名所使用的密鑰,才能驗證令牌的完整性。
通常,JWT 包含用户的“聲明”(claims)。這些聲明代表有關用户的元數據,API 可以使用這些元數據來授予權限或跟蹤提供令牌的用户。解碼令牌允許應用程序使用數據,而驗證允許應用程序信任 JWT 由可信來源生成。
讓我們看看如何在 Java 中解碼和驗證令牌。
3. 解碼 JWT
我們可以使用內置的 Java 函數來解碼令牌。
首先,讓我們將令牌分解為不同的部分:
String[] chunks = token.split("\\.");
請注意,傳遞給 String.split 的正則表達式使用轉義的 ‘.’ 字符,以避免 ‘.’ 表示“任意字符”。
我們的 chunks 數組現在應該包含兩個或三個元素,對應於 JWT 的部分。
接下來,讓我們使用 base64url 解碼器解碼 header 和 payload 部分:
Base64.Decoder decoder = Base64.getUrlDecoder();
String header = new String(decoder.decode(chunks[0]));
String payload = new String(decoder.decode(chunks[1]));
讓我們用一個 JWT(我們可以 在線解碼 進行比較)運行這段代碼:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkJhZWxkdW5nIFVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.qH7Zj_m3kY69kxhaQXTa-ivIpytKXXjZc1ZSmapZnGE
輸出將提供解碼後的 header 和 payload:
{"alg":"HS256","typ":"JWT"}{"sub":"1234567890","name":"Baeldung User","iat":1516239022}
如果 JWT 中僅定義了 header 和 payload 部分,我們已經完成了併成功解碼了信息。
4. 驗證 JWT
接下來,我們可以使用簽名部分來驗證頭信息和有效負載的完整性,以確保它們未被修改。
4.1. 依賴項
為了進行驗證,我們可以將 jjwt 添加到我們的 pom.xml 中:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.3</version>
</dependency>
我們應該注意的是,我們需要這個庫的版本必須從版本 0.7.0 之後。
4.2. 配置簽名算法和密鑰規範
為了開始驗證有效負載和頭信息,我們需要使用原始用於對令牌進行簽名所使用的簽名算法以及密鑰:
SignatureAlgorithm sa = SignatureAlgorithm.HS256;
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), sa.getJcaName());
在這個例子中,我們已經硬編碼了我們的簽名算法為 HS256。但是,我們可以解碼頭信息中的 JSON 並讀取 alg 字段來獲取這個值。
我們還應該注意的是,變量 secretKey 是密鑰的字符串表示形式。我們可能通過其配置或由發出 JWT 的服務提供的 REST API 向我們的應用程序提供此信息。
4.3. 執行驗證
現在我們已經有了簽名算法和密鑰,我們可以開始執行驗證。
JwtParser jwtParser = Jwts.parser()
.verifyWith(secretKeySpec)
.build();
try {
jwtParser.parse(token);
} catch (Exception e) {
throw new Exception("Could not verify JWT token integrity!", e);
}
讓我們分解一下。
首先,我們創建一個使用選擇的算法和密鑰的 JwtParser。然後我們使用 JwtParser 解析令牌。內部的 parse 方法的實現會生成一個新的簽名並將其與提供的簽名進行比較。如果它們相等,我們已驗證了頭信息和有效負載的完整性。如果拋出任何異常,則意味着令牌無效(它可能是格式錯誤、已過期等)。
5. 結論
在本文中,我們探討了 JWT 的結構以及如何將其解碼為 JSON。
然後,我們使用庫來驗證令牌的完整性,使用其簽名、算法和密鑰。