如何用Java通过对称密钥验证Jira Webhook事件?签名验证失败排查
尝试验证Jira Webhook发送到本地的事件有效性时,Java代码抛出错误:
JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
操作步骤:
- 通过API
https://api.atlassian.com/ex/jira//rest/api/2/webhook创建了监听评论删除事件的Webhook,设置密钥为"secret": "MySecretKey" - Webhook创建成功,能收到事件请求,请求头
Authorization字段包含JWT Token - 使用以下Java代码验证JWT,但验证失败:
public static boolean validateJiraJWT(String jwtToken, String secretKey) { try { Jws<Claims> jws = Jwts.parser() //.setSigningKey(jiraPublicKey).build() .setSigningKey(Keys.hmacShaKeyFor(secretKey.getBytes())).build() .parseClaimsJws(jwtToken); Claims claims = jws.getBody(); if (claims.getExpiration().before(new Date())) { System.out.println("JWT token has expired."); return false; } String issuer = claims.getIssuer(); if (!"jira".equals(issuer)) { System.out.println("Invalid issuer."); return false; } return true; } catch (Exception e) { // JWT token validation failed e.printStackTrace(); System.out.println(e.getMessage()); return false; } }
出现签名不匹配的问题,可从以下几个方向排查修复:
统一密钥编码格式
Jira的Webhook签名基于UTF-8编码生成,直接用secretKey.getBytes()会使用系统默认编码(比如GBK),导致密钥字节数组和Jira端不一致。修改为指定UTF-8编码:Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8))需提前导入
java.nio.charset.StandardCharsets包。正确提取JWT Token
请求头Authorization的格式是JWT <token>,如果直接传入整个字段内容,会包含前缀"JWT "导致解析失败。需先提取纯Token部分:String authHeader = request.getHeader("Authorization"); String jwtToken = authHeader.replace("JWT ", "");匹配Jira签名算法
确认Jira Webhook的签名算法:默认是HS256,你的代码用HMAC-SHA系列没问题;如果Jira配置了RS256算法,则需要启用代码中注释的公钥验证逻辑,同时去掉HMAC密钥的设置。同步服务器时间
JWT的过期时间基于Jira服务器时间,若本地服务器与Jira服务器时间差超过Token有效期(默认几分钟),会导致验证失败。检查两边系统时间是否同步。核对密钥一致性
再次确认Webhook配置的secret和代码传入的secretKey完全一致,包括大小写、空格、特殊字符,不能有任何拼写错误。
内容的提问来源于stack exchange,提问作者Afsar

