jjwt-api 单独引入会报 NoClassDefFoundError,因其仅为接口定义,需同时引入 jjwt-api、jjwt-impl 和 jjwt-jackson(或 jjwt-gson),且三者版本严格一致。

为什么 jjwt-api 单独引入会报 NoClassDefFoundError: io.jsonwebtoken.SignatureAlgorithm
因为 jjwt-api 只是接口定义,不带实现;真正干活的签名、解析、序列化逻辑在 jjwt-impl 里。JVM 加载接口时发现没实现类,直接炸。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 必须同时引入
jjwt-api+jjwt-impl+jjwt-jackson(或jjwt-gson),三者版本号严格一致,比如都用0.12.5 - Maven 里别只写
jjwt-api,那是给自己埋雷;老项目如果用了旧版jjwt(0.9.x),注意它把所有东西打包进一个 jar,和新版本模块化结构不兼容 - Spring Boot 3+ 默认用 Jakarta EE,而
jjwt0.12.x 要求jakarta.xml.bind,若提示ClassNotFoundException: javax.xml.bind.DatatypeConverter,说明你还在用 Java 8 的javax包——得切到jakarta.xml.bind实现,比如加jakarta.xml.bind:jakarta.xml.bind-api
Jwts.parserBuilder() 解析 Token 时抛 ExpiredJwtException 怎么安全捕获
JWT 过期不是异常场景,而是常规业务判断点。直接 try-catch ExpiredJwtException 没问题,但别把它当错误日志打 ERROR 级别——这是预期行为。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
setSigningKey指定密钥前,确保密钥类型匹配:HMAC 签名用SecretKeySpec,RSA 用PublicKey;传错类型会抛UnsupportedJwtException,不是过期异常 - 解析时别漏掉
requireExpiration或requireNotBefore这类校验链,否则即使 Token 过期也不会触发异常 - 推荐写法:先调用
parseClaimsJws(token).getBody(),再手动检查getExpiration()时间戳是否早于System.currentTimeMillis(),比全靠异常更可控
Spring Boot 中怎么让 JwtParser 自动注入且支持密钥轮换
Spring 官方不托管 JwtParser Bean,得自己配;密钥轮换也不是开个开关就行,得配合解析时的密钥选择逻辑。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 别直接 new
DefaultJwtParser,用Jwts.parserBuilder()构建,再通过setSigningKeyResolver注入自定义SigningKeyResolver - 密钥轮换的关键在于:从 Token header 里读
kid字段(getHeader().getKeyId()),再查本地缓存或远程配置中心拿到对应密钥;SigningKeyResolver.resolveSigningKey方法必须能处理 kid 为空或找不到的情况 - 注意
parserBuilder是线程安全的,可以单例复用;但每次解析都应新建ClaimsJws,别缓存解析结果
Token 解析后取 userId 字段总为 null,是 claims 命名不一致吗
大概率是 payload 里存的是 user_id 或 sub,但代码里硬写了 get("userId")。JWT 标准字段(如 sub, iss)有约定含义,自定义字段没强制命名规则。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 解析后先打印
claims.toString()看实际结构,别猜字段名;常见情况:前端传的是uid,后端却按userId取 - 用
getSubject()取sub最稳妥,OAuth2 场景下它通常就是用户唯一标识;如果非要自定义字段,统一用小写下划线(user_id)或 kebab-case(user-id),避免大小写混用引发歧义 - Spring Security 集成时,若用
BearerTokenAuthenticationFilter,默认从sub提取 principal,别指望它自动识别你私有的userId
密钥管理、kid 解析逻辑、claims 字段约定——这三块最容易在上线后出 silent fail,不是报错,而是返回空值或默认值,查起来特别费时间。










