【问题标题】:JWT signature does not match locally computed signatureJWT 签名与本地计算的签名不匹配
【发布时间】:2017-07-12 20:32:56
【问题描述】:

我正在使用

JwtBuilder builder = Jwts.builder()
                    .setId(user.getEmail())
                    .signWith(signatureAlgorithm, signingKey);

然后创建一个令牌

Jwts.parser().setSigningKey(secret).parse(token);

进行身份验证。当我在 JUnit 测试中运行它时,它工作正常。但是,当我通过 REST 调用对作为标头传递的令牌进行身份验证时,身份验证失败并出现 SignatureException。我已经验证了 HTTP 调用两端的令牌,并且令牌字符串是相同的。创建/验证的代码是静态的,因此,每一侧的秘密都是相同的。

【问题讨论】:

  • 你能发布一个示例令牌和密钥吗?
  • 静态密钥 = MacProvider.generateKey();签名算法签名算法=签名算法.HS256; byte[] apiKeySecretBytes = secret.getEncoded();密钥签名密钥 = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

标签: java jwt token


【解决方案1】:

static Key secret = MacProvider.generateKey(); 将在每次重新加载服务器时生成一个新的随机密钥,因为在加载类时会初始化静态变量

这意味着如果你发出一个 JWT,它只有在服务器没有重启的情况下才有效。你得到的SignatureException是因为签名密钥不同

您需要在第一代之后存储签名密钥secret.getEncoded(),并在模块启动时加载它

【讨论】:

  • 很棒的观察。但是,我在服务器上有一个实用程序类,它是对该静态密钥的唯一引用。它使用这个单一的引用来创建和验证操作。这就是它如此令人困惑的原因。
  • 只是为了检查没有其他隐藏问题:您可以在签名和验证之前打印密钥内容吗?例如System.out.println(DatatypeConverter.printHexBinary(secret.getEncoded()))
  • @stanlick,您在签名和验证时检查密码是否相同?
  • 你成功了兄弟!这是两个不同虚拟机的问题。即使生成的令牌在两端完全相同,但验证失败,因为令牌不是在执行验证的同一 VM 上生成的
  • 我也有类似的问题?你能告诉我如何解决它吗? stackoverflow.com/questions/45142800/…
【解决方案2】:

我遇到了同样的问题,我注意到在源中,每当他们转换签名密钥时,他们都会明确指定 UTF-8 编码。我尝试在解码令牌时更改编码:

 private Jws<Claims> decodeToken(String token) {
        return Jwts.parser()
                .setSigningKey(securityProperties.getTokenSecret().getBytes(Charset.forName("UTF-8")))
                .parseClaimsJws(token);
 }

并且在签署令牌时:

private String getSignedToken(UserDetailsAdapter user, List<String> roles, byte[] signingKey) {
        return Jwts.builder()
                .signWith(Keys.hmacShaKeyFor(signingKey), SignatureAlgorithm.HS512)
                .setHeaderParam("typ", securityProperties.getTokenType())
                .setIssuer(guiServerSecurityProperties.getTokenIssuer())
                .setAudience(guiServerSecurityProperties.getTokenAudience())
                .setSubject(user.getUsername())
                .setExpiration(new Date(System.currentTimeMillis() + 864000000))
                .claim("rol", roles)
                .compact();
    }

这是为我解决此问题的唯一方法。

【讨论】:

  • 这对我有用:Jwts.parser().setSigningKey(secret.getBytes(Charset.forName("UTF-8"))).parse(token);
【解决方案3】:

我遇到了类似的问题。就我而言,这是错误的令牌验证。我将符号设置为字节:

.signWith(SignatureAlgorithm.HS512, jwtConfig.getSecret().getBytes())

但是当我解析令牌并设置signKey时,我将其设置为字符串,而不是字节:

Jwts.parser().setSigningKey(signingKey).parseClaimsJws(this.token)

检查token时也要检查引号和空格,token的开头/结尾经常会有多余的空格/引号(使用trim()方法)

【讨论】:

  • 这正是我的问题所在。谢谢。
  • final String user = Jwts.parser() .setSigningKey(secret.getBytes()) .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) .getBody() .getSubject();跨度>
【解决方案4】:

我遇到了类似的问题。在我的情况下,两个键是相同的,但由于某种原因,我在引号内收到了一个令牌(例如 "Syasda.da3das.aDjty6" 而不仅仅是 Syasda.da3das.aDjty6)。

我花了很长时间才意识到这一点,因为大多数时候在 jwt.io 上进行测试时,我只是手动复制令牌而不使用括号来验证它。

token = token.replace("\"",""); 

删除这些引号解决了我的问题。希望这对其他人也有帮助。

【讨论】:

    【解决方案5】:

    我解决了在 URL REST 端点中修改 HOST 的问题。它有一个错误的主机,返回错误 HTTP 401 未授权。

    【讨论】:

      【解决方案6】:

      可能存在两种类型的问题:

      1. 每次运行函数secretKeyFor 时,Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); 都会生成一个不同的随机密钥。所以如果你想生成一个常量键,试试这个
       public SecretKey generalKey(){
              String stringKey = Global.JWT_SECRET;
              byte[] encodedKey =Base64.decodeBase64(stringKey);
              SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length,
                      "HmacSHA512");
              return key;
          }
      
      1. 在我的代码中,我收到了 ajax 传输的令牌,并且 JSON.stringify() 用于将 javasript 对象转换为字符串,它在原始字符串上包装了额外的配额,尝试消除配额,如 Nuper 所说.

      【讨论】:

        猜你喜欢
        • 2021-03-26
        • 2019-10-31
        • 1970-01-01
        • 2022-07-23
        • 1970-01-01
        • 1970-01-01
        • 2012-08-02
        • 1970-01-01
        • 2022-01-18
        相关资源
        最近更新 更多