NestJS JwtService验证JWT时读取错误nbf日期问题
问题:NestJS JwtService验证JWT时抛出NotBeforeError错误
问题重现
JWT初始化代码
import { JwtModule } from '@nestjs/jwt'; // ... JwtModule.register({ secret: 'mysecret', // TODO: 生产环境改用注入值 signOptions: { expiresIn: '1h' }, }),
JWT签名代码
import { JwtService, JwtSignOptions } from '@nestjs/jwt'; // ... constructor( private readonly jwtService: JwtService, ) {} // ... const userInfo = { email: user.email, firstName: user.firstName, lastName: user.lastName }; const accessTokenPayload = { iss: 'Auth Server 3.2', // 签发者 sub: user.id.toString(), // 主题(用户ID) aud: ['myClient'], // 受众(接收方) exp: (new Date()).getTime() + 24 * 60 * 60 * 1000, // 过期时间(毫秒级时间戳) nbf: (new Date()).getTime() - 60 * 60 * 1000, iat: new Date().getTime(), // 签发时间(毫秒级时间戳) jti: randomBytes(32).toString('hex'), // JWT唯一标识 data: { userInfo } } const accessToken = await this.jwtService.signAsync(accessTokenPayload);
验证代码及错误
try { await this.jwtService.verifyAsync(accessToken); } catch(e) { console.log('Error', e) }
抛出错误:
Error NotBeforeError { name: 'NotBeforeError', message: 'jwt not active', date: +055303-05-11T16:49:49.000Z // 注意这里的nbf解析出了错误的未来日期 }
问题原因
JWT标准中,nbf(生效时间)、exp(过期时间)、iat(签发时间)字段要求的是Unix秒级时间戳,但你使用new Date().getTime()获取的是毫秒级时间戳(13位数字)。
验证时,@nestjs/jwt底层依赖的jsonwebtoken库会把这个毫秒数当成秒数解析,相当于把实际时间放大了1000倍,原本的2023年被解析成了55303年,自然会抛出"jwt not active"错误。
解决方案
方案1:手动将毫秒时间戳转为秒级
把所有时间字段的值除以1000并取整,转换成符合标准的秒级时间戳:
const currentTime = Math.floor(Date.now() / 1000); // 转成秒级时间戳 const accessTokenPayload = { iss: 'Auth Server 3.2', sub: user.id.toString(), aud: ['myClient'], exp: currentTime + 24 * 60 * 60, // 24小时后过期(秒) nbf: currentTime - 60 * 60, // 1小时前生效(秒) iat: currentTime, jti: randomBytes(32).toString('hex'), data: { userInfo } } const accessToken = await this.jwtService.signAsync(accessTokenPayload);
方案2:让JwtService自动处理标准时间字段
推荐使用这种方式,避免手动处理时间戳的错误。在JwtModule注册时配置默认参数,签名时只传递业务相关数据:
// JWT模块初始化 JwtModule.register({ secret: 'mysecret', signOptions: { expiresIn: '24h', // 自动设置exp字段 notBefore: '-1h', // 自动设置nbf字段(允许1小时前生效) issuer: 'Auth Server 3.2', // 自动设置iss字段 audience: ['myClient'] // 自动设置aud字段 }, }), // 签名代码 const accessTokenPayload = { sub: user.id.toString(), jti: randomBytes(32).toString('hex'), data: { userInfo } } const accessToken = await this.jwtService.signAsync(accessTokenPayload);
此时JwtService会自动生成符合标准的iat、nbf、exp字段,无需手动计算时间戳。
内容的提问来源于stack exchange,提问作者Lorraine Ram-El
相关产品推荐
相关产品推荐

