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有效性且不可信任。
核心问题分析
- 签名密钥不固定:
getSigningKey()每次调用都会生成新的HS512密钥,导致生成Token和验证Token时用的密钥完全不一致,直接引发签名不匹配。 - Issuer字段不一致:验证阶段强制要求
issuer为http://localhost:8080,但生成Token时未设置该字段,验证逻辑会直接失败。 - 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
相关产品推荐
相关产品推荐

