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

Spring应用生成的JWT无法在NodeJS中验证,求问题排查

JWT跨Java Spring与NodeJS验证失败问题排查

问题描述

我开发了一个基于Java Spring的认证微服务,负责生成JWT,对应的JWT工具类代码如下:

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class JwtTokenProvider {

    @Value("${jwt.secret}")
    private String APP_SECRET;

    @Value("${jwt.expiresIn}")
    private long EXPIRES_IN;

    public String generateJwtToken(Authentication auth) {
        CustomUserDetails userDetails = (CustomUserDetails) auth.getPrincipal();
        Date expireDate = new Date(new Date().getTime() + EXPIRES_IN);

        return Jwts.builder()
                .setSubject(Long.toString(userDetails.getId()))
                .setIssuedAt(new Date())
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(token);
            return !isTokenExpired(token);
        } catch (SignatureException e) {
            return false;
        }
    }
}

在application.properties中配置了256位密钥:

# JWT
jwt.secret=92502665b0711fc987959b89d3958a26b2f8f60a8963263360192b89e7c0fa44
jwt.expiresIn=604800

该JWT在Spring应用内验证正常,但在NodeJS服务中使用jsonwebtoken库验证时,始终返回Invalid Signature错误,无法获取负载内容。NodeJS验证代码如下:

const jwt = require('jsonwebtoken');

const getAccessToRoute = (req, res, next) => {
    const { JWT_SECRET_KEY } = process.env;

    if (!isTokenIncluded(req)) {
        // return token not found
    }

    // extract the token from header
    const accessToken = getAccessTokenFromHeader(req);

    jwt.verify(accessToken, JWT_SECRET_KEY, async (err, decodedUserInfos) => {
        console.log("DECODED USER INFOS", decodedUserInfos); // undefined
        if (err) {
            console.log(err.message)
            return res.status(401).json({ message: "Invalid or expired token" })
        }

        req.loggedUser = {
            id: decodedUserInfos.id,
        };

        next();
    })
};

.env文件中配置的JWT密钥:

# JWT
JWT_SECRET_KEY=92502665b0711fc987959b89d3958a26b2f8f60a8963263360192b89e7c0fa44

两端密钥与签名算法完全一致,请问问题出在哪里?


问题原因与解决方案

核心原因:两端密钥处理逻辑不一致

Java的JJWT库在使用signWith(SignatureAlgorithm.HS256, APP_SECRET)时,会直接将字符串密钥作为原始UTF-8字符串用于HMAC签名;而NodeJS的jsonwebtoken库默认会把传入的字符串密钥当作Base64编码的密钥进行解码,再用于签名验证。这就导致两端实际使用的签名密钥不匹配,触发Invalid Signature错误。

两种解决方案

方案一:修改NodeJS代码适配Java的密钥处理逻辑

在jwt.verify方法中明确指定编码格式为utf8,告知库直接使用原始字符串密钥,无需Base64解码;同时注意Java端用setSubject设置的用户ID,在JWT负载中对应字段是sub而非id:

jwt.verify(accessToken, JWT_SECRET_KEY, { algorithms: ['HS256'], encoding: 'utf8' }, async (err, decodedUserInfos) => {
    console.log("DECODED USER INFOS", decodedUserInfos);
    if (err) {
        console.log(err.message)
        return res.status(401).json({ message: "Invalid or expired token" })
    }

    req.loggedUser = {
        id: decodedUserInfos.sub,
    };

    next();
})

方案二:修改Java代码适配NodeJS的默认处理逻辑

将Java端的字符串密钥转换为Base64编码后再用于签名,确保两端密钥处理逻辑一致:

import java.util.Base64;

// ... 其他代码不变

return Jwts.builder()
        .setSubject(Long.toString(userDetails.getId()))
        .setIssuedAt(new Date())
        .setExpiration(expireDate)
        .signWith(SignatureAlgorithm.HS256, Base64.getEncoder().encodeToString(APP_SECRET.getBytes()))
        .compact();

修改后NodeJS端保持现有代码即可,无需额外配置。

额外验证点

  • 确认两端密钥完全一致,检查.env和application.properties中的密钥是否存在空格、换行或大小写差异。
  • 用JWT解析工具查看生成的token的header部分,确认alg字段为HS256,确保签名算法一致。

内容的提问来源于stack exchange,提问作者anileates

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 23:00:06