什麼是 JWT?
JWT 是一種緊湊的、URL 安全的方式,用於在各方之間傳輸信息。這些信息可以被驗證和信任,因為它們是數字簽名的。JWT 通常用於:
身份驗證(最常見的用途)
信息交換
訪問授權
JWT 的結構
一個 JWT 由三部分組成,用點號分隔:
1. 頭部(Header)
頭部通常由兩部分組成:
- 令牌類型(即 "JWT")
- 簽名算法(如 HMAC SHA256 或 RSA)
2. 載荷(Payload)
載荷包含聲明(claims),聲明是關於實體(通常是用户)和其他數據的聲明。有三種類型的聲明:
註冊聲明:預定義的聲明,如 iss(簽發者)、exp(過期時間)、sub(主題)等
公共聲明:可以自定義的聲明
私有聲明:自定義聲明,用於在同意使用它們的各方之間共享信息
3. 簽名(Signature)
簽名用於驗證消息在傳輸過程中沒有被篡改。對於使用 HMAC SHA256 算法的令牌
如何生成JWT
1.添加maven依賴
<!-- jwt -->
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.0</version>
</dependency>
2.編寫生成公鑰私鑰類
package com.shopping.shopping_user_service.util;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.lang.JoseException;
// 生成公鑰和私鑰
public class GenerateJwtDemo {
public static void main(String[] args) throws JoseException {
// 生成 RSA 2048 密鑰對
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
// 獲取公鑰
final String publicKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
// 獲取私鑰
final String privateKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
//輸出公鑰
System.out.println(publicKeyString);
//輸出私鑰
System.out.println(privateKeyString);
}
}
編寫jWT生成工具類
package com.shopping.shopping_user_service.util;
import lombok.SneakyThrows;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
public class JwtUtils {
// 私鑰
public final static String PRIVATE_JSON = "{\"kty\":\"RSA\",\"n\":\"xKCf7Wks7H8zt5YbeQYlNl6rzwsnwS0hy_LL8u2Tg_Bv8lbzVrSptRILGRNdbbhio66pf-xMaPnr3-90PhNMU8uFfyk5Oo0QbteohPBt767OqW9jECFRoBii61PxvRX68zlTUhqMsY4-TzIZPFQl9zNxG7xzluwDKW7edRl-wFwRUaRUQUkHsJ838EeiBiGfGPWRvQmptnzqiW1zchBXH_hPdPlQxG3jlT76p6RXHSrFXnEueK_jLjCrkIt4XQqdoh4sFHQ6r5vH3m784DfSgUXD8LlJE0xpv0xNTHp6kJCtruRTxxlWqo1mpfh8fv1YxsuiowEfVimBmNK0mOQnzQ\",\"e\":\"AQAB\",\"d\":\"Ap3xqvb-pFIoFc4vZOu2RJQ7fKi1GX7Yh46edMgBMd0aHFNYb0O9y31nPOjzUiHFXFDXjC6QsMf8wfD9rtLSbf4UM4ZQru0QNi10HnrVt74vnXUwUBdlbvhM-M1X9xCLE8AkUgAs9bMDNMohqFolXDYoz_nw3uYav6ssVkol0_kPwBFt8Gqqblmud6OXxN2rPRwO00efwKQ7Th5iBm39I6H7NDF2cqAfddx--iGui7C9y1Cg_0eoDtIvbs4mekiWpicuqKMlYxXHlkwz6YAwQ-hrWh6zn4YfkDvgkwYUlm3ndpPVavBfh_74RKq08SNYxqHHfSXfzlAXGeiD6jH_8Q\",\"p\":\"4BfEML29f2aFTQupNVaGvQ29ZS39jg7hkaHfAfNg3qgwxIFHyY-kZH8m-yRv45Grv9gSb_EVQuGra-9sOqA_B7Lm4QO7E75yajMtWWzEXpXO5_hw3nfLgYeWjchJa552Sanm6ax-HEJ5zrBylJxjVs3mrOgqc0ozKwqAL9jgEf0\",\"q\":\"4J-9EGiWEclfA_dPuMlNz2YKxmWt9dzhb0EET_Hgrxhulra-nuFEcfUD3inc3WMOeKGdPX4X_uJHGZ_sdQHR_SxitlEhCrLkWCStgl-JiJUyQI2nzdDGEpiUkPQKbmbXucxnu-ighRDVzxiHj7xc1uiEEkshhsqd8sS7M3a9rhE\",\"dp\":\"w9shxrPe7n1tHiSF9C82vf9HPCpRBJru_Tirz2mmjZQUY3rWgk0AEjGroS58eDo2EQtJOftMaNMR3tk4D5lE3Xa4IYwAMsZt-3HVPeY5Tq3CU64o_9dVz1Tw-eRGfz_VBJbxUeRHqG1VRpei3U496H03EoIrr-33ALRByw_S7wU\",\"dq\":\"DNUTV_yhliKK6w8V3tihOVf7BZqJdaJIvrJYLLkEvPwIVVPbT_hOkLQOpIJ_u6YNDZcuBHVPqaADr9MTDxwrgusmOIQp_xJ5OQ_fWbNbiBH97PIqw_dJWYad9in67pOxf0vOIU4I3ZE4pbwhAnUgWKav5Nul7q1kmJnkl-wQqgE\",\"qi\":\"h8SOTkbfKxPrzAGKbbGVZvRGM36Z_EENC2v_rOEU45qGDdczCTe3ZUbAJyN71trGnAzF_sPlkiq2FsjdKiuFhF4fxH0TCpmGISvS3aCDrK9WtGoZx5wytBYOmWK9cx8aDnNtlWXlpv3hktYeUS5IBhulmsiQdjLs0c05qZLvdbs\"}";
// 公鑰
public final static String PUBLIC_JSON = "{\"kty\":\"RSA\",\"n\":\"xKCf7Wks7H8zt5YbeQYlNl6rzwsnwS0hy_LL8u2Tg_Bv8lbzVrSptRILGRNdbbhio66pf-xMaPnr3-90PhNMU8uFfyk5Oo0QbteohPBt767OqW9jECFRoBii61PxvRX68zlTUhqMsY4-TzIZPFQl9zNxG7xzluwDKW7edRl-wFwRUaRUQUkHsJ838EeiBiGfGPWRvQmptnzqiW1zchBXH_hPdPlQxG3jlT76p6RXHSrFXnEueK_jLjCrkIt4XQqdoh4sFHQ6r5vH3m784DfSgUXD8LlJE0xpv0xNTHp6kJCtruRTxxlWqo1mpfh8fv1YxsuiowEfVimBmNK0mOQnzQ\",\"e\":\"AQAB\"}";
/**
* 生成token
*
* @param userId 用户id
* @param username 用户名字
* @return
*/
@SneakyThrows
public static String sign(Long userId, String username) {
// 1、 創建jwtclaims jwt內容載荷部分
JwtClaims claims = new JwtClaims();
// 是誰創建了令牌並且簽署了它
claims.setIssuer("itbaizhan");
// 令牌將被髮送給誰
claims.setAudience("audience");
// 失效時間長 (分鐘)
claims.setExpirationTimeMinutesInTheFuture(60 * 24);
// 令牌唯一標識符
claims.setGeneratedJwtId();
// 當令牌被髮布或者創建現在
claims.setIssuedAtToNow();
// 再次之前令牌無效
claims.setNotBeforeMinutesInThePast(2);
// 主題
claims.setSubject("shopping");
// 在JWT令牌中存儲業務相關的用户信息
claims.setClaim("userId", userId);
claims.setClaim("username", username);
// 2、簽名
JsonWebSignature jws = new JsonWebSignature();
//賦值載荷
jws.setPayload(claims.toJson());
// 3、jwt使用私鑰簽署
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(PRIVATE_JSON)).getPrivateKey();
jws.setKey(privateKey);
// 4、設置關鍵 kid
jws.setKeyIdHeaderValue("keyId");
// 5、設置簽名算法
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
// 6、生成jwt
String jwt = jws.getCompactSerialization();
return jwt;
}
/**
* 解密token,獲取token中的信息
*
* @param token
*/
@SneakyThrows
public static Map<String, Object> verify(String token) {
// 1、引入公鑰
PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(PUBLIC_JSON)).getPublicKey();
// 2、使用jwtcoonsumer 驗證和處理jwt
JwtConsumer jwtConsumer = new JwtConsumerBuilder()
.setRequireExpirationTime() //過期時間
.setAllowedClockSkewInSeconds(30) //允許在驗證得時候留有一些餘地 計算時鐘偏差 秒
.setRequireSubject() // 主題生命
.setExpectedIssuer("itbaizhan") // jwt需要知道誰發佈得 用來驗證發佈人
.setExpectedAudience("audience") //jwt目的是誰 用來驗證觀眾
.setVerificationKey(publicKey) // 用公鑰驗證簽名 驗證密鑰
.setJwsAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256))
.build();
// 3、驗證jwt 並將其處理為 claims
try {
JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
return jwtClaims.getClaimsMap();
} catch (Exception e) {
return new HashMap();
}
}
//驗證
public static void main(String[] args) {
// 生成
String baizhan = sign(1001L, "baizhan");
System.out.println(baizhan);
Map<String, Object> stringObjectMap = verify(baizhan);
System.out.println(stringObjectMap.get("userId"));
System.out.println(stringObjectMap.get("username"));
}
}
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。