在现代 Web 开发和微服务架构中,身份验证和授权是至关重要的环节。JSON Web Token(JWT)作为一种开放标准已经成为实现无状态身份验证的热门选择。本文将深入探讨 JWT
·
什么是 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"));
}
}
更多推荐
所有评论(0)