JJWT 入门:Java 生态最主流的 JWT 库
一篇讲清楚 JJWT 是什么、为什么后端几乎都用它、以及怎么在一个 Spring Boot 项目里把它用起来。
JWT 是什么?先搞懂这个前置概念
JWT(JSON Web Token)是一种无状态的身份凭证。传统的 Session 登录,用户状态存在服务器内存里,服务器一重启或者一扩容就麻烦;JWT 把用户身份直接编码成一个带签名的字符串发给客户端,之后每次请求带上它,服务器只要验签就能确认身份,不需要存任何会话状态。
一个 JWT 长这样:
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMDAxIiwiZXhwIjoxNzUwMDAwMDAwfQ.7Fb3x...
用 . 分隔成三段:Header(用什么算法签名)、Payload(存了什么信息,比如用户 ID、过期时间)、Signature(用密钥对前两段做的签名)。前两段只是 Base64 编码,不是加密,谁都能解开看,所以不要往 Payload 里放密码这类敏感信息;签名的作用是保证内容没被篡改——改了任何一个字符,验签就会失败。
为什么选 JJWT
Java 生态里能处理 JWT 的库不少,JJWT 是最主流的选择,原因有三个:
- API 符合直觉。构建、解析、验签都是链式调用,看方法名就知道在干什么。
- 类型安全。解析出来的是强类型的 Claims 对象,不用自己手写 JSON 解析。
- 维护活跃。从 jjwt 0.x 一路迭代到 0.12.x,安全补丁跟进及时。
Spring Boot 项目引入依赖(以 0.12.x 为例):
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
三个包的分工很清晰:jjwt-api 是编译期接口,jjwt-impl 是运行时实现,jjwt-jackson 负责序列化。实现依赖设成 runtime,能防止代码不小心依赖到实现细节。
生成一个 Token
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
public class JwtUtil {
// 密钥至少 32 字节(HS256 要求),生产环境从配置读取,不要硬编码
private static final SecretKey KEY =
Keys.hmacShaKeyFor("my-super-secret-key-at-least-32-bytes!".getBytes(StandardCharsets.UTF_8));
/** 签发 token:塞入用户 ID,24 小时后过期 */
public String createToken(Long userId) {
return Jwts.builder()
.subject(String.valueOf(userId))
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + 24 * 3600_000))
.signWith(KEY)
.compact();
}
}
链式调用的每一步都在「声明这个 token 是什么样的」:subject 放主体标识,expiration 设过期时间,signWith 用密钥签名。最后 compact() 输出字符串。
解析与验签
/** 验签 + 解析,一步完成;签名不对或已过期会抛异常 */
public Long parseToken(String token) {
Claims claims = Jwts.parser()
.verifyWith(KEY)
.build()
.parseSignedClaims(token)
.getPayload();
return Long.valueOf(claims.getSubject());
}
关键是这一步同时做了两件事:验签(确认没被篡改)和检查过期(exp 过了直接抛 ExpiredJwtException)。所以业务代码里只要 try-catch 一下,就能区分「token 无效」和「token 过期」两种情况给出不同提示。
在 Spring Boot 里落地
典型的用法是配一个过滤器:请求进来先检查 Authorization 头里的 token,验过了就把用户信息放进请求上下文,放行;验不过直接返回 401。
客户端登录 → 服务端签发 token → 客户端存起来(localStorage/cookie)
→ 每次请求带 Authorization: Bearer <token>
→ 服务端过滤器验签 → 通过则处理业务
几个生产环境的要点:
- 密钥管理:密钥泄露等于所有人都能伪造身份,务必放在环境变量或受限配置文件里,不进 Git。
- 过期时间要短:access token 通常 30 分钟到 24 小时;想做「记住我」,用 refresh token 机制而不是把 access token 拉长到一个月。
- HTTPS 是前提:token 明文传输一旦被截获就等于账号被冒用,生产环境必须全程 HTTPS。
- 无法主动作废是 JWT 的天然短板:签出去的 token 在过期前一直有效。需要「立即踢下线」能力时,配合 Redis 存黑名单,或者干脆短过期 + refresh 轮换。
小结
JJWT 解决的是「Java 里怎么方便又安全地生成和校验 JWT」这一件事。理解了三段结构、验签即校验这两个核心,API 只是顺水推舟。本站的登录鉴权就是这条链路:登录签发 token,之后所有请求靠它无状态通行——你在多个设备同时登录互不干扰,服务器也不用存任何会话。