背景介绍
在项目开发中,使用第三方库能够大大提高开发效率。然而,随着时间的推移,库的版本不断更新,有时会引入不兼容的变化。我在使用 JJWT(Java JSON Web Token)库时就遇到了这样的情况。
问题描述
我之前一直使用的是 JJWT 0.9.1 版本,这段代码是在本科大三的时候写的。之后每次需要自定义 JJWT 工具时,我都是直接复制这段代码。直到2024年,在一个新的系统中,我遇到了一个 bug,提示无法找到 .SignatureAlgorithm
包。
排查过程
首先,我去 Maven 仓库查看了 JJWT 的坐标,重新安装了 Maven 依赖,但问题依旧存在。这时我决定更新 JJWT 的版本,看看是否能解决问题。
在搜索和阅读了相关资料后,我发现 JJWT 库在较新的版本(0.10.0 及以上)中进行了模块化拆分。具体如下:
-
jjwt-api: 包含所有的 API 接口和核心类(包括
SignatureAlgorithm
)。 - jjwt-impl: 包含 API 的具体实现。
- jjwt-jackson 或 jjwt-gson: 提供 JSON 序列化和反序列化支持。
在 API 方面也发生了这些变化:
- SignatureAlgorithm:SignatureAlgorithm 类仍然是核心功能的一部分,但现在包含在 jjwt-api 模块中。
- ParserBuilder:引入了 parserBuilder() 方法,替代了旧的 parser() 方法,现在构建一个 JwtParserBuilder 实例,增强了 JWT 解析的灵活性和可配置性。
以及在兼容性和使用方面
- 向后兼容:JJWT 0.10.1 旨在保持向后兼容,但需要更改项目中依赖的包含和使用方式。
- 安装:项目现在需要在 Maven 或 Gradle 配置中明确包含 jjwt-api、jjwt-impl 和 jjwt-jackson 或 jjwt-gson 模块。
解决方案
了解了这些变化后,我调整了 Maven 依赖,分别导入了 jjwt-api
、jjwt-impl
和 jjwt-jackson
模块。重新编译项目,发现之前的 SignatureAlgorithm
问题消失了。
调整后的代码如下:
public class JwtUtil {
/**
* 生成jwt
* 使用Hs256算法, 私匙使用固定秘钥
*
* @param secretKey jwt秘钥
* @param ttlMillis jwt过期时间(毫秒)
* @param claims 设置的信息
* @return
*/
public static String createJWT(String secretKey, long ttlMillis, Map<String, Object> claims) {
// 指定签名的时候使用的签名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成JWT的时间
long expMillis = System.currentTimeMillis() + ttlMillis;
Date exp = new Date(expMillis);
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
// 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
.setClaims(claims)
// 设置签名使用的签名算法和签名使用的秘钥
.signWith(signatureAlgorithm, secretKey.getBytes(StandardCharsets.UTF_8))
// 设置过期时间
.setExpiration(exp);
return builder.compact();
}
/**
* Token解密
*
* @param secretKey jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
* @param token 加密后的token
* @return
*/
public static Claims parseJWT(String secretKey, String token) {
// 得到DefaultJwtParser
Claims claims = Jwts.parser()
// 设置签名的秘钥
.setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
// 设置需要解析的jwt
.parseClaimsJws(token).getBody();
return claims;
}
}
思考与总结
JJWT 的模块化设计将不同功能分开,使得依赖管理更加清晰。对于仅需要部分功能的项目,减少了不必要的依赖。 在之后做项目的时候尽量在项目的构建工具锁定依赖的版本,或者使用 LTS 版本,会减少很多莫名其妙的 bug。