欢迎光临
我们一直在努力

10分钟上手 轻量级JWT库

JWT 是 Java Web 项目最常用的身份认证方案。但市面上大多数 JWT 库要么依赖太重,要么安全默认值不够严格。

引入依赖,直接开始用

Spring Boot 2.x 项目:

<dependency>
<groupId>com.gitee.apanlh</groupId>
<artifactId>apanlh-common</artifactId>
<version>2.0.6</version>
</dependency>

Spring Boot 3.x 项目:

<dependency>
<groupId>com.gitee.apanlh</groupId>
<artifactId>apanlh-common</artifactId>
<version>3.0.6</version>
</dependency>

JDK 8+ 可用,零强制第三方依赖。引了就能用。

最简单的用法:3 分钟上手

签发 Token

import com.gitee.apanlh.util.algorithm.jwt.JwtBuilder;
import com.gitee.apanlh.util.algorithm.jwt.JwtAlgorithm;
import java.util.concurrent.TimeUnit;

// 选一个算法,配置密钥
JwtAlgorithm alg = JwtAlgorithm.HS256("your-secret-key-at-least-32-bytes!!");

// 构建 Token
String token = JwtBuilder.builder(alg)
.subject("user-123")
.issuer("my-app")
.claim("role", "admin")
.expiresAt(1, TimeUnit.HOURS) // 1 小时过期
.build();

System.out.println(token);
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTEyMyIsImlzcyI6Im15LWFwcCIsImV4cCI6MT…

验证 Token

import com.gitee.apanlh.util.algorithm.jwt.JwtVerifier;
import com.gitee.apanlh.util.algorithm.jwt.JwtVerifyResult;

JwtVerifyResult result = JwtVerifier.builder(alg)
.expectedIssuer("my-app")
.build()
.verify(token);

result.getSubject(); // "user-123"
result.getIssuer(); // "my-app"
result.getClaim("role"); // "admin"

签发和验证用的是同一个 alg 实例,算法和密钥绑在一起,不会出现配置不一致的问题。

一行快捷验证

// 一行搞定,等价于 builder(alg).build().verify(token)
JwtVerifyResult result = JwtVerifier.verify(token, alg);

三大算法族

HMAC 对称加密

最常用,签发和验证用同一把密钥:

// HS256(密钥至少 32 字节)
JwtAlgorithm alg = JwtAlgorithm.HS256("your-secret-key-at-least-32-bytes!!");

// HS384(密钥至少 48 字节)
JwtAlgorithm alg = JwtAlgorithm.HS384("your-secret-key-at-least-48-bytes-long-for-hs384!!");

// HS512(密钥至少 64 字节)
JwtAlgorithm alg = JwtAlgorithm.HS512("your-secret-key-at-least-64-bytes-long-for-hs512-algorithm!!");

密钥太短会自动拒绝:

JwtAlgorithm.HS256("short"); // 报错:HS256 requires at least 32 bytes

RSA 非对称加密

适合签发服务和验证网关分离的场景:

// 签发端:持有私钥,可签发可验证
JwtAlgorithm signer = JwtAlgorithm.RSA256(privateKey, publicKey);
JwtAlgorithm signerOnly = JwtAlgorithm.RSA256Signer(privateKey);

// 验证端:只持有公钥,只能验证
JwtAlgorithm verifier = JwtAlgorithm.RSA256Verifier(publicKey);

同样支持 RS384 和 RS512,RSA 密钥至少 2048 位,不足的会自动拒绝。

ECDSA 非对称加密

适合对 Token 体积有要求的场景(签名更短):

// ES256(P-256 曲线)
JwtAlgorithm alg = JwtAlgorithm.ECDSA256(privateKey, publicKey);

// ES384(P-384 曲线)
JwtAlgorithm alg = JwtAlgorithm.ECDSA384(privateKey, publicKey);

// ES512(P-521 曲线)
JwtAlgorithm alg = JwtAlgorithm.ECDSA512(privateKey, publicKey);

内置 DER 转 JWS 签名格式转换,不需要 BouncyCastle,曲线不匹配会自动拒绝。

完整签发示例

JwtAlgorithm alg = JwtAlgorithm.HS256("your-secret-key-at-least-32-bytes!!");

String token = JwtBuilder.builder(alg)
// 标准 claims
.subject("user-123") // 主题
.issuer("my-app") // 签发者
.audience("web-client") // 受众
.expiresAt(1, TimeUnit.HOURS) // 签发后 1 小时过期
.notBefore(10, TimeUnit.MINUTES) // 签发后 10 分钟才生效
.generateJti() // 自动生成 UUID 作为 JWT ID

// 自定义 claims
.claim("role", "admin") // 字符串
.claim("level", 10) // 数值
.claim("vip", true) // 布尔值

// 额外 Header
.headerParam("kid", "key-v1")

.build();

完整验证示例

JwtAlgorithm alg = JwtAlgorithm.HS256("your-secret-key-at-least-32-bytes!!");

JwtVerifyResult result = JwtVerifier.builder(alg)
// 验证签发者、受众、主题
.expectedIssuer("my-app")
.expectedAudience("web-client")
.expectedSubject("user-123")

// 验证自定义 claims
.expectedClaims(Map.of("role", "admin"))

// 时钟偏差容忍(服务端时钟可能略有偏差)
.allowedClockSkew(60, TimeUnit.SECONDS)

// 限制最大 Token 有效期(防止签发超长有效期的 Token)
.maxTokenLifetime(24, TimeUnit.HOURS)

// 自定义拦截器:Token 黑名单 / JTI 防重放
.interceptor((header, payload) -> {
String jti = payload.getJwtId();
if (blacklist.contains(jti)) {
throw new JwtException("token has been revoked");
}
})

.build()
.verify(token);

// 获取验证结果
result.getSubject(); // "user-123"
result.getIssuer(); // "my-app"
result.getClaim("role"); // "admin"
result.getPayload(); // 完整 Payload 对象

安全方面

none 算法攻击默认防御

构建验证器时,算法白名单默认只允许构建时指定的算法。你用什么算法构建验证器,Token 就必须用什么算法签名,其他算法一律拒绝。

算法混淆攻击默认防御

RSA 公钥是公开的,攻击者可以用它当 HMAC 密钥签一个 HS256 的 Token。但由于白名单锁死了算法,HS256 的 Token 在第一关就会被拒绝。

时序攻击防御

签名比较使用 MessageDigest.isEqual() 逐字节恒定时间比较,不因字节差异提前返回,防止通过响应时间差异猜测签名。

弱密钥自动拒绝

  • HMAC 密钥:HS256 至少 32 字节,HS384 至少 48 字节,HS512 至少 64 字节
  • RSA 密钥:至少 2048 位
  • ECDSA:必须匹配对应曲线(ES256 用 P-256,ES384 用 P-384,ES512 用 P-521)

密钥太短或曲线不对会直接拒绝,不会签发成功。

Token 格式安全

  • 不用正则 split 分割 Token(用 indexOf,无 ReDoS 风险)
  • 每段限制 8KB 以内,防止超长 Payload 消耗内存
  • alg 和 typ 是保留字段,不能通过 headerParam() 注入

Claim 安全

  • 自定义 claim 只接受 String、Number、Boolean,拒绝复杂对象
  • 解析 Header 时会丢弃所有非标准参数(kid、jku、x5u 等),防止注入

兼容遗留系统:Lenient 宽松模式

如果项目还在用短密钥或弱 RSA 密钥,提供了 Lenient 后缀的工厂方法:

JwtAlgorithm.HS256Lenient("short"); // 跳过长度校验
JwtAlgorithm.RSA256Lenient(privateKey, publicKey); // 跳过 2048 位校验
JwtAlgorithm.ECDSA256Lenient(privateKey, publicKey); // 跳过曲线校验

新系统必须用标准模式。

实用功能

自动生成 JTI

// UUID
JwtBuilder.builder(alg).generateJti().build();

// 雪花 ID
JwtBuilder.builder(alg).generateSnowFlakeJti().build();

// 指定机器节点的雪花 ID
JwtBuilder.builder(alg).generateSnowFlakeJti(5, 31).build();

不适合的场景

  • 不支持 JWE(加密 JWT)
  • 不支持 jku、x5u 等 Header 参数(解析时会被丢弃)
  • 不替代 Spring Security OAuth2 Resource Server

项目地址: https://gitee.com/apanlh/pan-common

项目文档: https://gitee.com/apanlh/pan-common/wikis/首页

赞(0)
未经允许不得转载:171主机测评 » 10分钟上手 轻量级JWT库
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址