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

部署在Digital Ocean的Spring Security应用出现间歇性403错误求助

部署在Digital Ocean的Spring Security应用出现间歇性403错误求助

嘿,我仔细看了你的问题和代码,感觉这个间歇性403的问题大概率出在JWT签名密钥的生成方式上,咱们来捋一捋:

核心问题:动态生成的密钥导致签名不匹配

你代码里的SECRET_KEY是用Jwts.SIG.HS512.key().build()生成的,这行代码的关键问题是——每次你的Spring Boot应用启动时,都会生成一个全新的随机密钥:

private static final SecretKey SECRET_KEY = Jwts.SIG.HS512.key().build();
  • 本地运行时,你一般不会频繁重启应用,所以密钥从头到尾都是同一个,生成的JWT用这个密钥验证当然100%通过;
  • 但部署到Digital Ocean之后,情况就不一样了:
    比如DO的应用平台可能会因为健康检查触发重启、自动扩容启动新实例,或者服务器本身重启,每一次重启都会生成一个新的密钥。这时候,你用旧密钥生成的JWT,请求到用新密钥运行的实例时,就会触发SecurityException,出现你看到的“JWT signature does not match”错误;而如果请求刚好落到没重启、用旧密钥的实例上,验证就正常。这就是为什么会出现间歇性403的原因!

你说在jwt.io上验证能通过,应该是你用了当前运行实例的密钥去校验的,这时候肯定匹配,但只要实例一重启,密钥变了,旧的JWT就直接失效了。

解决方法:把密钥固定下来

要彻底解决这个问题,你需要把动态生成的密钥改成固定的、全局一致的密钥,不管应用重启多少次、多少实例运行,都用同一个密钥:

  1. 先生成一个固定的HS512密钥:
    你可以跑一段临时代码生成一个符合要求的密钥,然后把结果存起来:
    // 这段代码只需要跑一次,把输出的Base64字符串存好
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.security.Keys;
    import java.util.Base64;
    
    public class GenerateJwtSecret {
        public static void main(String[] args) {
            SecretKey fixedKey = Jwts.SIG.HS512.key().build();
            System.out.println(Base64.getEncoder().encodeToString(fixedKey.getEncoded()));
        }
    }
    
  2. 在应用里通过配置加载这个固定密钥:
    • 先在application.properties(或yml)里加配置:
      jwt.secret=刚才生成的Base64字符串
      jwt.expiration-ms=86400000 # 这里用你原来的过期时间就行
      
    • 然后修改你的JWT工具类代码,从配置里读取密钥:
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.stereotype.Component;
      import io.jsonwebtoken.*;
      import io.jsonwebtoken.security.Keys;
      import java.util.Base64;
      import java.util.Date;
      
      @Component
      public class JwtUtils {
          @Value("${jwt.secret}")
          private String jwtSecret;
      
          @Value("${jwt.expiration-ms}")
          private int jwtExpirationMs;
      
          // 从配置的Base64字符串获取固定密钥
          private SecretKey getSecretKey() {
              byte[] keyBytes = Base64.getDecoder().decode(jwtSecret);
              return Keys.hmacShaKeyFor(keyBytes);
          }
      
          public String generateTokenFromUsername(String username) {
              return Jwts.builder()
                      .subject(username)
                      .issuedAt(new Date())
                      .expiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
                      .signWith(getSecretKey(), Jwts.SIG.HS512)
                      .compact();
          }
      
          public boolean validateJwtToken(String authToken) {
              try {
                  Jwts.parser()
                          .verifyWith(getSecretKey())
                          .build()
                          .parseSignedClaims(authToken)
                          .getPayload();
                  return true;
              } catch (SecurityException e) {
                  logger.error("Invalid JWT signature: {}", e.getMessage());
              } catch (MalformedJwtException e) {
                  logger.error("Invalid JWT token: {}", e.getMessage());
              } catch (ExpiredJwtException e) {
                  logger.error("JWT token is expired: {}", e.getMessage());
              } catch (UnsupportedJwtException e) {
                  logger.error("JWT token is unsupported: {}", e.getMessage());
              } catch (IllegalArgumentException e) {
                  logger.error("JWT claims string is empty: {}", e.getMessage());
              }
              return false;
          }
      
          // 你的logger定义和其他方法保持不变
      }
      
  3. 最后,确保Digital Ocean上的所有应用实例都使用同一个jwt.secret配置——最好用DO的环境变量来设置这个密钥,不要硬编码到代码里,更安全也更灵活。

额外排查点(如果上面的方法没解决)

  • 检查DO的部署是不是多实例:如果开了自动扩容,必须保证所有实例用同一个密钥;
  • 看应用日志有没有频繁重启的记录:如果应用动不动就重启,那密钥肯定会频繁变化;
  • 确认密钥的编码有没有问题:生成的Base64字符串要完整复制,不要有空格或换行;

按照这个方法改完,应该就能解决这个烦人的间歇性403问题了,祝你顺利!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 10:24:33