JWT(JSON Web Token)源码分析
Java - JWT的简单介绍和使用
Java JWT:原理、机制及案例示范
什么是JWT?
1.1 JWT的基本概念
JWT(JSON Web Token)是一种用于在各方之间传递JSON格式信息的紧凑、URL安全的令牌(Token)。JWT的主要作用是验证用户身份或权限。它由三部分组成:
- Header(头部):标识令牌的类型和加密算法。
- Payload(载荷):包含了实际的身份信息及其他数据。
- Signature(签名):使用头部和载荷生成的签名,用于验证数据完整性和来源的可靠性。
1.2 JWT的结构
JWT的结构由三部分组成,它们通过点号(.)进行分隔,格式如下:
Header.Payload.Signature
具体每一部分的内容如下:
- Header(头部): 通常包含两部分信息:令牌的类型(JWT)和签名的算法(如HMAC SHA256或RSA)。
{ "alg": "HS256", "typ": "JWT" }
- Payload(载荷): 载荷是JWT的主体部分,包含了需要传输的数据。通常包含以下几种常见的声明(Claims)
iss:签发者 exp:过期时间 sub:主题 aud:接收者 iat:签发时间 nbf:在此之前不可用
- Signature(签名): 签名部分是用来验证消息的完整性,并确保其未被篡改。签名的生成方式如下:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
代码示例
org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-test test com.auth0 java-jwt 3.19.1
package tool; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import org.junit.Test; import java.util.Calendar; import java.util.HashMap; import java.util.List; public class JwtToolTest { // 秘钥,你可以随便取,可以取的难一点 public static final String SECRET = "ASD!@#F^%A"; //加密 private static String tokenCreate() { HashMap headers = new HashMap(); // 过期时间,60s Calendar expires = Calendar.getInstance(); expires.add(Calendar.SECOND, 600); return JWT.create() // 第一部分Header .withHeader(headers) // 第二部分Payload .withClaim("userId", 20) .withClaim("userName", "LJJ") //相同的key, 会覆盖前面的数据 .withClaim("userName", List.of("aaa", "bb")) .withSubject("hahahha") .withExpiresAt(expires.getTime()) // 第三部分Signature .sign(Algorithm.HMAC256(SECRET)); } @Test public void testTokenCreate() { System.out.println(tokenCreate()); } //解密 @Test public void testReadJWT() { // 创建一个验证的对象 JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build(); DecodedJWT verify = jwtVerifier.verify(tokenCreate()); System.out.println(verify.getClaim("userId").asInt()); System.out.println(verify.getClaim("userName")); System.out.println(verify.getSubject()); System.out.println("过期时间:" + verify.getExpiresAt()); } }
JWTCreator.java
JWT.create() 会创建 JWTCreator
public static JWTCreator.Builder create() { return JWTCreator.init(); } static JWTCreator.Builder init() { return new Builder(); } Builder() { this.payloadClaims = new HashMap(); this.headerClaims = new HashMap(); }
初始化两个map ,用来存储header 和 payload数据
提供的with方法最终都是往 payloadClaims 、 headerClaims 中添加key:value数据
特殊的key数据
public interface PublicClaims { //Header String ALGORITHM = "alg"; String CONTENT_TYPE = "cty"; String TYPE = "typ"; String KEY_ID = "kid"; //Payload String ISSUER = "iss"; String SUBJECT = "sub"; String EXPIRES_AT = "exp"; String NOT_BEFORE = "nbf"; String ISSUED_AT = "iat"; String JWT_ID = "jti"; String AUDIENCE = "aud"; }
加密
private String sign() throws SignatureGenerationException { String header = Base64.getUrlEncoder().withoutPadding().encodeToString(headerJson.getBytes(StandardCharsets.UTF_8)); String payload = Base64.getUrlEncoder().withoutPadding().encodeToString(payloadJson.getBytes(StandardCharsets.UTF_8)); byte[] signatureBytes = algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8)); String signature = Base64.getUrlEncoder().withoutPadding().encodeToString((signatureBytes)); return String.format("%s.%s.%s", header, payload, signature); }
verify(解密校验)
// JWTVerifier.java @Override public DecodedJWT verify(String token) throws JWTVerificationException { DecodedJWT jwt = new JWTDecoder(parser, token); return verify(jwt); } @Override public DecodedJWT verify(DecodedJWT jwt) throws JWTVerificationException { verifyAlgorithm(jwt, algorithm); algorithm.verify(jwt); verifyClaims(jwt, claims); return jwt; } private void verifyClaims(DecodedJWT jwt, Map claims) throws TokenExpiredException, InvalidClaimException { for (Map.Entry entry : claims.entrySet()) { if (entry.getValue() instanceof NonEmptyClaim) { assertClaimPresent(jwt.getClaim(entry.getKey()), entry.getKey()); } else { verifyClaimValues(jwt, entry); } } }
最终根据特殊的key来校验token
private void verifyClaimValues(DecodedJWT jwt, Map.Entry expectedClaim) { switch (expectedClaim.getKey()) { // We use custom keys for audience in the expected claims to differentiate between validating that the audience // contains all expected values, or validating that the audience contains at least one of the expected values. case AUDIENCE_EXACT: assertValidAudienceClaim(jwt.getAudience(), (List) expectedClaim.getValue(), true); break; case AUDIENCE_CONTAINS: assertValidAudienceClaim(jwt.getAudience(), (List) expectedClaim.getValue(), false); break; case PublicClaims.EXPIRES_AT: assertValidDateClaim(jwt.getExpiresAt(), (Long) expectedClaim.getValue(), true); break; case PublicClaims.ISSUED_AT: assertValidDateClaim(jwt.getIssuedAt(), (Long) expectedClaim.getValue(), false); break; case PublicClaims.NOT_BEFORE: assertValidDateClaim(jwt.getNotBefore(), (Long) expectedClaim.getValue(), false); break; case PublicClaims.ISSUER: assertValidIssuerClaim(jwt.getIssuer(), (List) expectedClaim.getValue()); break; case PublicClaims.JWT_ID: assertValidStringClaim(expectedClaim.getKey(), jwt.getId(), (String) expectedClaim.getValue()); break; case PublicClaims.SUBJECT: assertValidStringClaim(expectedClaim.getKey(), jwt.getSubject(), (String) expectedClaim.getValue()); break; default: assertValidClaim(jwt.getClaim(expectedClaim.getKey()), expectedClaim.getKey(), expectedClaim.getValue()); break; } }
- Signature(签名): 签名部分是用来验证消息的完整性,并确保其未被篡改。签名的生成方式如下:
- Payload(载荷): 载荷是JWT的主体部分,包含了需要传输的数据。通常包含以下几种常见的声明(Claims)
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。