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

JJWT解析篡改JWT未抛异常?与jwt.io结果不一致问题咨询

问题描述

我正在学习用于身份认证的JWS,采用Java库jjwt进行实现。生成JWT令牌字符串后,我在其末尾添加了一个字符,意外发现jjwt库未抛出SignatureException就完成了解析;但相同的篡改令牌在jwt.io调试器中显示无效。测试多个密钥后发现,部分密钥下该问题存在,部分则正常抛出异常,想咨询这是否是库或算法的问题,是否需要使用更复杂的密钥?

测试代码

public class TestJwt {
    // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi";          // WORKING !!!
    // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi";         // WORKING !!!
    // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236345345weec3wdn3i432oi";            // WORKING !!!
    // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236345432oi";                         // NOT WORKING
    // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv523632222222245y6454524524tef45432oi"; // NOT WORKING
    
    private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi"; // WORKING
    private static final Key key = Keys.hmacShaKeyFor(JWT_SECRET_KEY.getBytes());
    

    public static void main(String args[]) {
        
        String token = 
        Jwts.builder()
        
        .claim("name", "RANDOM")
        .claim("surname", "ANOTHER_RANDOM")
    
        .signWith(key)
        .compact();
        
        System.out.println("TOKEN BEFORE");
        System.out.println(token);
        
        // Adding single character to jwt and still working!!
        token = token+"7";
        
        System.out.println("TOKEN AFTER");
        System.out.println(token);
        
        
        Claims claims = Jwts.parserBuilder()
                .setSigningKey(key).build()             
                .parseClaimsJws(token).getBody();
        
        claims.forEach((k,v)->{
            System.out.println("________________________");
            System.out.println(k + " : "+v);
            
        });
        
        System.out.println("________________________"); 
    }
}

测试输出

TOKEN BEFORE
eyJhbGciOiJIUzM4NCJ9.eyJuYW1lIjoiUkFORE9NIiwic3VybmFtZSI6IkFOT1RIRVJfUkFORE9NIn0.evntuAcZ0Urnv-5QniShmENKNBSzrjoxeNWN0uW-sy-qXzC-G2PJyi316m9LqQH9
TOKEN AFTER
eyJhbGciOiJIUzM4NCJ9.eyJuYW1lIjoiUkFORE9NIiwic3VybmFtZSI6IkFOT1RIRVJfUkFORE9NIn0.evntuAcZ0Urnv-5QniShmENKNBSzrjoxeNWN0uW-sy-qXzC-G2PJyi316m9LqQH97
________________________
name : RANDOM
________________________
surname : ANOTHER_RANDOM
________________________
问题分析与解决

核心原因:Base64URL的容错特性与JJWT默认逻辑

这个问题和密钥复杂度无关,本质是Base64URL编码的容错机制以及JJWT的默认解析逻辑导致的:

  1. JWT的签名部分是Base64URL编码的HMAC哈希值,Base64URL允许省略末尾填充符=,且部分解析器会自动忽略编码字符串末尾的无效字符——因为Base64按6位一组解码,多余字符会被直接截断。
  2. 当你添加的字符属于Base64URL的无效字符时,JJWT会自动截断这部分字符,用原签名的哈希值做验证,自然会通过;而jwt.io的验证逻辑更严格,会检查签名部分的Base64URL合法性,直接判定篡改令牌无效。

解决方案:启用严格的Base64URL验证

JJWT默认解析器对Base64URL的容错性较高,你可以配置解析器强制开启严格模式,确保任何篡改都会触发异常:

Claims claims = Jwts.parserBuilder()
        .setSigningKey(key)
        // 启用严格的Base64URL解码,拒绝任何无效字符或长度不匹配的情况
        .base64UrlDecoder(Base64UrlDecoder.STANDARD)
        .build()             
        .parseClaimsJws(token).getBody();

启用Base64UrlDecoder.STANDARD后,只要令牌被篡改(包括末尾加字符),就会抛出MalformedJwtException或SignatureException。

额外建议

  • 生产环境仍需使用足够长度的随机密钥:HS256至少256位(32字节),HS384需384位(48字节),HS512需512位(64字节),避免暴力破解风险。
  • 统一后端验证规则,不要依赖第三方工具的验证逻辑,确保生产环境的验证严格且一致。

内容的提问来源于stack exchange,提问作者Kaustubh Dixit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 23:10:43