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

SvelteKit生成JWT在Spring Boot验证失败的问题及解决

JWT签名验证失败问题解决与优化方案(SvelteKit + Spring Boot)

问题场景

我开发了一套前后端分离架构:前端基于SvelteKit实现OAuth2用户认证,登录时生成JWT存入Cookie;后端用Spring Boot作为数据访问层,接收前端发送的Bearer Token并验证JWT合法性。但验证时出现错误:
JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.

初始SvelteKit生成JWT代码

const login = await event.locals.auth();

if (login) {
    const authCookie = event.cookies.get('authCookie');

    if (new Date(login.expires) > new Date()) {
        if (!authCookie) {
            const jwt = jsonwebtoken.sign({ email: login.user?.email }, `${process.env.AUTH_JWT}`);

            console.log(jwt)

            const expirationDate = new Date();
            expirationDate.setMonth(expirationDate.getMonth() + 1);

            event.cookies.set('authCookie', jwt, {
                httpOnly: true,
                secure: true,
                sameSite: 'strict',
                path: '/',
                expires: expirationDate
            });
        }
    } else {
        if (!authCookie) {
            event.cookies.delete('authCookie', { path: '/' });
        }
    }
} else {
    event.cookies.delete('authCookie', { path: '/' });
}

初始Spring Boot验证JWT代码

@PostMapping("/api/createNewFavorite")
public FavoriteModel CreateNewFavorite(@RequestBody FavoriteModel favoriteModel , @RequestHeader HttpHeaders httpHeaders){

    String jwtBearerToken = Objects.requireNonNull(httpHeaders.get("authorization")).get(0);
    String jwt = jwtBearerToken.split(" ")[1];

    String  secretString = "u2nv8bk5c3y9pl7h6f9o28jn0c4xsmua";

    SecretKey authKey = Keys.hmacShaKeyFor(secretString.getBytes());

    Jws<Claims> parsed = Jwts.parser().verifyWith(authKey).build().parseSignedClaims(jwt);

    Claims claims = parsed.getPayload();

    System.out.println(claims);

    return createFavorite.CreateNewFavorite(favoriteModel);
}

问题根因

SvelteKit中process.env.AUTH_JWT环境变量未正确加载,导致签名JWT使用的密钥与Spring Boot端硬编码的密钥不一致,最终引发签名验证失败。

解决方法

1. 修复SvelteKit密钥加载问题

暂时直接使用硬编码密钥(后续需排查环境变量加载逻辑),同时修正原代码中无效的Cookie删除判断:

if (login) {
    const authCookie = event.cookies.get('authCookie');

    if (new Date(login.expires) > new Date()) {
        if (!authCookie) {
            // 替换为实际密钥字符串,后续需修复环境变量加载
            const jwt = jsonwebtoken.sign({ email: login.user?.email }, `u2nv8bk5c3y9pl7h6f9o28jn0c4xsmua`);

            console.log(jwt)

            const expirationDate = new Date();
            expirationDate.setMonth(expirationDate.getMonth() + 1);

            event.cookies.set('authCookie', jwt, {
                httpOnly: true,
                secure: true,
                sameSite: 'strict',
                path: '/',
                expires: expirationDate
            });
        }
    } else {
        // 修正逻辑:存在Cookie时才执行删除
        if (authCookie) {
            event.cookies.delete('authCookie', { path: '/' });
        }
    }
} else {
    event.cookies.delete('authCookie', { path: '/' });
}

2. 优化Spring Boot验证逻辑

指定UTF-8字符集生成密钥,避免编码差异;添加异常捕获处理验证失败场景:

@PostMapping("/api/createNewFavorite")
public FavoriteModel CreateNewFavorite(@RequestBody FavoriteModel favoriteModel, @RequestHeader HttpHeaders httpHeaders) {
    try {
        String jwtBearerToken = Objects.requireNonNull(httpHeaders.get("authorization")).get(0);
        String jwt = jwtBearerToken.split(" ")[1];

        String secretString = "u2nv8bk5c3y9pl7h6f9o28jn0c4xsmua";
        // 指定UTF-8字符集,确保密钥字节数组一致
        SecretKey authKey = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));

        Jws<Claims> parsed = Jwts.parser().verifyWith(authKey).build().parseSignedClaims(jwt);
        Claims claims = parsed.getPayload();

        System.out.println(claims);
        return createFavorite.CreateNewFavorite(favoriteModel);
    } catch (SignatureException e) {
        throw new IllegalArgumentException("JWT签名验证失败: " + e.getMessage());
    } catch (NullPointerException e) {
        throw new IllegalArgumentException("请求头中缺少Authorization令牌");
    }
}

优化建议

1. SvelteKit环境变量优化

  • 遵循Vite规范,环境变量名以VITE_开头(如VITE_AUTH_JWT),存储在根目录.env文件中
  • 使用import.meta.env.VITE_AUTH_JWT读取变量,替代process.env
  • 确保生产环境变量通过部署平台注入,避免硬编码

2. Spring Boot JWT验证优化

  • 避免硬编码密钥:将密钥配置在application.properties中,通过@Value注入
    jwt.secret=u2nv8bk5c3y9pl7h6f9o28jn0c4xsmua
    
    @Value("${jwt.secret}")
    private String secretString;
    
  • 封装JWT工具类:将解析、验证逻辑抽离为独立组件,减少重复代码
  • 全局异常处理:使用@RestControllerAdvice统一处理JWT相关异常,返回标准化JSON响应
  • 完善验证规则:添加令牌过期时间、签发者等验证逻辑,提升安全性

3. 安全优化

  • 明确指定JWT签名算法(如HS256),避免依赖默认值
    SvelteKit端:
    const jwt = jsonwebtoken.sign(
      { email: login.user?.email },
      import.meta.env.VITE_AUTH_JWT,
      { algorithm: 'HS256', expiresIn: '30d' } // 自动设置过期时间,无需手动计算Cookie有效期
    );
    
  • 动态配置Cookie的secure属性:生产环境仅在HTTPS下启用,开发环境可关闭

内容的提问来源于stack exchange,提问作者W.A Rajinda

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 06:46:02