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

JWT签名异常求助:签名不匹配,无法验证JWT有效性

JWT签名不匹配问题解决

原代码

@Service
@RequiredArgsConstructor
public class JwtUtil {

    private SecretKey getSigningKey() {
        return Jwts.SIG.HS512.key().build();
    }

    public String generateToken(SecurityMember securityMember) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, securityMember.getUsername());
    }

    public String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder().claims(claims).subject(subject).issuedAt(new Date(System.currentTimeMillis()))
                .expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(getSigningKey())
                .compact();
    }

    public Boolean validateToken(String token, SecurityMember securityMember) {
        final String username = extractUserName(token);
        return (username.equals(securityMember.getUsername()) && !isTokenExpired(token));
    }

    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date(System.currentTimeMillis()));
    }

    public Date extractExpiration(String token) {
        return extractClaims(token,Claims::getExpiration);
    }

    public String extractUserName(String token) {
        return extractClaims(token,Claims::getSubject);
    }

    private <T> T extractClaims(String token, Function<Claims, T> claimsResolver) {
        final Jwe<Claims> claims = extractAllClaims(token);
        return claimsResolver.apply(claims.getPayload());
    }

    private Jwe<Claims> extractAllClaims(String token) {
        try {
            return Jwts.parser()
                            .requireIssuer("http://localhost:8080")
                            .verifyWith(getSigningKey())
                            .build()
                            .parse(token).accept(Jwe.CLAIMS);
        } catch (JwtException ex) {
            throw new JwtException("WRONG JWT"+ex.getMessage(),ex);
        }
    }
}

问题描述

抛出异常:io.jsonwebtoken.security.SignatureException: JWT签名与本地计算的签名不匹配,无法断言JWT有效性且不可信任。


核心问题分析

  1. 签名密钥不固定:getSigningKey()每次调用都会生成新的HS512密钥,导致生成Token和验证Token时用的密钥完全不一致,直接引发签名不匹配。
  2. Issuer字段不一致:验证阶段强制要求issuer为http://localhost:8080,但生成Token时未设置该字段,验证逻辑会直接失败。
  3. JWT类型混淆:HS512是签名算法(生成的是JWS格式Token),但代码用Jwe<Claims>处理,JWE是加密格式,类型不匹配导致解析逻辑错误。

修复后的代码

@Service
@RequiredArgsConstructor
public class JwtUtil {
    // 固定签名密钥,确保生成和验证用同一个密钥
    private final SecretKey signingKey = Jwts.SIG.HS512.key().build();

    private SecretKey getSigningKey() {
        return signingKey;
    }

    public String generateToken(SecurityMember securityMember) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, securityMember.getUsername());
    }

    public String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .claims(claims)
                .subject(subject)
                .issuedAt(new Date(System.currentTimeMillis()))
                .expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .issuer("http://localhost:8080") // 添加与验证一致的issuer
                .signWith(getSigningKey())
                .compact();
    }

    public Boolean validateToken(String token, SecurityMember securityMember) {
        final String username = extractUserName(token);
        return (username.equals(securityMember.getUsername()) && !isTokenExpired(token));
    }

    private boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date(System.currentTimeMillis()));
    }

    public Date extractExpiration(String token) {
        return extractClaims(token, Claims::getExpiration);
    }

    public String extractUserName(String token) {
        return extractClaims(token, Claims::getSubject);
    }

    private <T> T extractClaims(String token, Function<Claims, T> claimsResolver) {
        final Jws<Claims> claims = extractAllClaims(token);
        return claimsResolver.apply(claims.getPayload());
    }

    private Jws<Claims> extractAllClaims(String token) {
        try {
            return Jwts.parser()
                    .requireIssuer("http://localhost:8080")
                    .verifyWith(getSigningKey())
                    .build()
                    .parseSignedClaims(token); // 用正确的方法解析签名类型JWT
        } catch (JwtException ex) {
            throw new JwtException("WRONG JWT" + ex.getMessage(), ex);
        }
    }
}

关键修复点

  • 固定密钥:将密钥改为类成员变量,避免每次调用生成新密钥;生产环境建议从配置文件读取密钥字符串,再转换为SecretKey,防止重启后密钥变更。
  • 同步Issuer:生成Token时添加与验证逻辑一致的issuer字段。
  • 修正解析逻辑:使用parseSignedClaims()解析签名类型JWT,将返回类型改为Jws<Claims>,匹配HS512签名的Token格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 03:23:32