JWT(JSON Web Token)源码分析

Java - JWT的简单介绍和使用
Java JWT:原理、机制及案例示范

什么是JWT?

1.1 JWT的基本概念

JWT(JSON Web Token)是一种用于在各方之间传递JSON格式信息的紧凑、URL安全的令牌(Token)。JWT的主要作用是验证用户身份或权限。它由三部分组成:

  1. Header(头部):标识令牌的类型和加密算法。
  2. Payload(载荷):包含了实际的身份信息及其他数据。
  3. 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)

代码示例

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.19.1</version>
</dependency>
java 复制代码
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<String, Object> 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

java 复制代码
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数据

java 复制代码
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";

}

加密

java 复制代码
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(解密校验)

java 复制代码
// 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<String, Object> claims) throws TokenExpiredException, InvalidClaimException {
    for (Map.Entry<String, Object> entry : claims.entrySet()) {
        if (entry.getValue() instanceof NonEmptyClaim) {
            assertClaimPresent(jwt.getClaim(entry.getKey()), entry.getKey());
        } else {
            verifyClaimValues(jwt, entry);
        }
    }
}

最终根据特殊的key来校验token

java 复制代码
private void verifyClaimValues(DecodedJWT jwt, Map.Entry<String, Object> 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<String>) expectedClaim.getValue(), true);
            break;
        case AUDIENCE_CONTAINS:
            assertValidAudienceClaim(jwt.getAudience(), (List<String>) 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<String>) 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;
    }
}
相关推荐
背太阳的牧羊人11 天前
JWT 鉴权机制 通俗易懂解释版本
jwt·鉴权机制
Amd79418 天前
FastAPI安全认证中的依赖组合
测试用例·fastapi·jwt·依赖注入·权限校验·安全认证·组合依赖
草海桐24 天前
golang 的github.com/dgrijalva/jwt-go包
golang·jwt·jwt-go
可爱的霸王龙1 个月前
SpringBoot整合JWT
java·后端·jwt
csdn_aspnet1 个月前
在 .NET 9.0 Web API 中实现 Scalar 接口文档及JWT集成
jwt·.net9·scalar
图图图图爱睡觉2 个月前
大白话解释认证JWT是什么 有什么用 怎么用
jwt
lixww.cn3 个月前
ASP.NET Core对JWT的封装
asp.net core·jwt·authorize
lixww.cn3 个月前
ASP.NET Core JWT Version
asp.net core·jwt·filter·identity
lixww.cn3 个月前
ASP.NET Core JWT
asp.net core·jwt