Spring Cloud APIGateway JWT认证签名不匹配错误求助
Spring Cloud JWT认证签名不匹配问题
问题场景
基于jjwt最新版本实现Spring Cloud JWT认证,认证成功后将Token写入响应头,但API网关校验时抛出以下异常:
io.jsonwebtoken.security.SignatureException: JWT签名与本地计算的签名不匹配。无法确认JWT的有效性,不应信任该JWT。
相关代码如下:
API Gateway 校验代码(AuthorizationHeaderFilter.java)
private boolean isJwtValid(String jwt) { boolean isValid = false; String subject = null; String tokenSecret = env.getProperty("token.secret"); SecretKey signingKey = Keys.hmacShaKeyFor(tokenSecret.getBytes()); JwtParser parser = Jwts.parser().verifyWith(signingKey).build(); try { Jwt<Header, Claims> parsedToken = (Jwt<Header, Claims>) parser.parseUnsecuredClaims(jwt); subject = parsedToken.getPayload().getSubject(); } catch(Exception ex) { System.out.println("ERRRRRRRRROR"); } if(subject !=null && !subject.isEmpty()) { isValid = true; } return isValid; }
用户服务Token生成代码(AuthenticationFilter.java)
@Override protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException, ServletException { String userName = ((User) auth.getPrincipal()).getUsername(); UserDto userDto = userService.getUserDetailsByEmail(userName); String tokenSecret = environment.getProperty("token.secret"); byte[] secretKeyBytes = Base64.encodeBase64(tokenSecret.getBytes()); SecretKey secretKey = Keys.hmacShaKeyFor(secretKeyBytes); Instant now = Instant.now(); String token = Jwts.builder().subject(userDto.getUserId()).expiration( Date.from(now.plusMillis(Long.parseLong(environment.getProperty("token.expiration_time"))))) .issuedAt(Date.from(now)) .signWith(secretKey) .compact(); res.addHeader("token", token); res.addHeader("userId", userDto.getUserId()); }
问题根源
Token生成与校验环节的密钥处理逻辑不一致:
- 用户服务生成Token时,对
token.secret做了Base64编码后再生成签名密钥 - API网关校验时,直接使用
token.secret的原始字节生成校验密钥
两者密钥处理逻辑不统一,导致签名校验失败。此外,网关侧使用parseUnsecuredClaims方法会跳过签名校验,仅解析Payload,即使签名错误也会返回有效,存在安全隐患。
修复方案
1. 统一密钥处理逻辑(二选一)
方案A:修改用户服务,移除Base64编码
删除用户服务中对密钥的Base64编码步骤,直接使用原始密钥字节生成密钥:
// 原代码 // byte[] secretKeyBytes = Base64.encodeBase64(tokenSecret.getBytes()); // 修改后 byte[] secretKeyBytes = tokenSecret.getBytes(); SecretKey secretKey = Keys.hmacShaKeyFor(secretKeyBytes);
方案B:修改API网关,添加Base64解码
如果需要保留Base64编码的密钥处理,网关侧需对原始密钥进行Base64解码:
// 原代码 // SecretKey signingKey = Keys.hmacShaKeyFor(tokenSecret.getBytes()); // 修改后 byte[] decodedSecret = Base64.decodeBase64(tokenSecret.getBytes()); SecretKey signingKey = Keys.hmacShaKeyFor(decodedSecret);
2. 修复网关签名校验逻辑
将网关侧的parseUnsecuredClaims替换为parseSignedClaims,确保签名校验生效:
// 原代码 // Jwt<Header, Claims> parsedToken = (Jwt<Header, Claims>) parser.parseUnsecuredClaims(jwt); // 修改后 Jwt<Header, Claims> parsedToken = parser.parseSignedClaims(jwt);
内容的提问来源于stack exchange,提问作者Robin Singh
相关产品推荐
相关产品推荐

