You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JJWT解析JWT的JUnit测试报NoSuchMethodError异常排查

问题:调用JJWT的parserBuilder()方法抛出NoSuchMethodError

问题场景

基于JJWT 0.11.5版本实现JWT有效性校验逻辑,核心逻辑是读取配置密钥后调用Jwts.parserBuilder()构建解析器解析令牌,捕获解析抛出的JwtException后封装为401异常返回。
对应实现代码、单元测试、依赖配置如下:

JWT校验方法

public void validateExpiryDate(String token) {
    try {
        byte[] secret = configProperties.getTokenSecret().getBytes(StandardCharsets.UTF_8);
        SecretKey hmacKey = new SecretKeySpec(secret, HMAC_SHA_256_ALGORITHM);

        Jwts.parserBuilder()
                .setSigningKey(hmacKey)
                .build()
                .parseClaimsJws(token);
    } catch (JwtException e) {
        throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, INVALID_TOKEN_ERR);
    }
}

过期场景单元测试

@Test
void validatorShouldThrow_when_tokenIsExpired() {
    Date issuedAt = Date.from(Instant.now());
    Date expiresAt = Date.from(issuedAt.toInstant().minus(10, ChronoUnit.MINUTES));
    String jwt = generator.createJWT(JWT_TEST_NAME, JWT_TEST_MAIL, JWT_TEST_SUBJECT, JWT_TEST_ID, issuedAt, expiresAt, CONFIG_PROPERTIES_SECRET);

    assertThrows(ResponseStatusException.class, () -> validator.validateExpiryDate(jwt));
}

测试用JWT生成工具方法

public String createJWT(String name, String email, String subject, String id, Date issuedAt, Date expiresAt, String secret) {
    return Jwts.builder()
            .claim("name", name)
            .claim("email", email)
            .setSubject(subject)
            .setId(id)
            .setIssuedAt(issuedAt)
            .setExpiration(expiresAt)
            .signWith(SignatureAlgorithm.HS256, Base64.getDecoder().decode(secret))
            .compact();
}

Gradle依赖配置(Kotlin DSL)

runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")

运行测试时未得到预期的ResponseStatusException,反而抛出如下错误:

org.opentest4j.AssertionFailedError: Unexpected exception type thrown ==> expected: <org.springframework.web.server.ResponseStatusException> but was: <java.lang.NoSuchMethodError>
Caused by: java.lang.NoSuchMethodError: io.jsonwebtoken.Jwts.parserBuilder()Lio/jsonwebtoken/JwtParserBuilder;

已经通过gradle -q dependencies确认所有显式声明的JJWT依赖版本为0.11.5,无旧版本依赖混入,且清空了本地Maven仓库C:\Users\<user>\.m2\repository后问题仍复现。


根因

Jwts.parserBuilder()是JJWT 0.10.0及以上版本才提供的API,0.9.x及更早版本使用Jwts.parser()作为解析入口。出现该方法不存在的错误,核心原因是运行时实际加载的io.jsonwebtoken.Jwts类来自0.9.x及更早版本的JJWT包,和编译时使用的0.11.5版本API不兼容。
之前的排查存在两个关键遗漏:

  1. 缓存清理位置错误:Gradle默认不使用.m2目录下的Maven缓存,自身的依赖缓存存放在C:\Users\<user>\.gradle\caches路径下,清空Maven缓存不会影响Gradle已拉取的依赖文件。
  2. 依赖作用域配置问题:runtimeOnly配置的依赖仅在主源码集运行时加入类路径,部分Gradle版本、IDEA测试运行器不会将该作用域的依赖正确加入测试类路径,导致运行测试时JVM优先加载了其他路径下的旧版JJWT类(比如Spring Boot默认依赖管理引入的旧版JJWT、其他第三方库传递引入的0.9.x版本JJWT)。

解决步骤

按以下顺序操作即可修复问题:

  • 调整JJWT依赖作用域,将jjwt-impl和jjwt-jackson从runtimeOnly改为implementation,保证编译、测试、运行全阶段都能加载正确版本的类,修改后配置如下:
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
implementation("io.jsonwebtoken:jjwt-impl:0.11.5")
implementation("io.jsonwebtoken:jjwt-jackson:0.11.5")
  • 强制刷新Gradle依赖,执行以下命令清理构建缓存并重新拉取依赖:
gradle clean build --refresh-dependencies

如果命令执行后仍有问题,手动删除C:\Users\<user>\.gradle\caches下所有jjwt相关的缓存文件夹即可。

  • 在IDEA的Gradle面板点击刷新按钮,重新同步项目依赖。
  • 修复隐藏的逻辑问题:当前JWT生成和校验的密钥处理逻辑不一致——生成JWT时对传入的secret做了Base64解码,校验时却直接取secret字符串的UTF-8字节数组作为密钥,依赖问题解决后会触发签名无效异常,需要将两边的密钥处理逻辑统一。

内容的提问来源于stack exchange,提问作者Hristo Naydenov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 03:57:37