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

NestJS中JWT令牌验证失败,返回500内部服务器错误求助

NestJS JWT令牌无效且返回500错误的排查与解决

问题描述

使用NestJS及基于jsonwebtoken的JWT包时,生成的令牌始终无效,且接口返回500内部服务器错误。

代码实现

AuthService 中的 login 函数

async login(email: string, password: string, isAdmin?: boolean, isVIP?: boolean){
  let user = await this.usersService.findByEmail(email);
  if(!user){
    throw new NotFoundException('No user with this email could be found.');
  }
  const isEqual = await bcrypt.compare(password, user.password);
  if(!isEqual){
    throw new BadRequestException('Email and password do not match');
  }
  const secret = 'secretkey';
  const payload = {email: user.email, userId: user._id.toString()}
  const token = this.jwtService.sign(payload, {secret, expiresIn: '1h'});
  return [email, isAdmin, isVIP, token];
}

JwtAuthGuard 验证逻辑

import { BadRequestException, CanActivate, ExecutionContext, Inject } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt/dist";
import { JwtConfigService } from "src/config/jwtconfig.service";

export class JwtAuthGuard implements CanActivate {
    constructor(@Inject(JwtService) private jwtService: JwtService){}
    canActivate(context: ExecutionContext) {
        const request = context.switchToHttp().getRequest();

        const authHeader = request.get('Authorization');
        if (!authHeader) {
            throw new BadRequestException('Not authorized');
        }
    const token = authHeader.split(' ')[1];
    let decodedToken;
    try {
        decodedToken = this.jwtService.verify(token, {secret: 'secretkey'});
    } catch (err) {
        throw new Error('Cannot verify token.')
    }
    if(!decodedToken){
        throw new BadRequestException('Not authenticated')
    }
  request.userId = decodedToken.userId;
  console.log({decodedToken, token});
  return request.userId;
};
}

UsersModule 中的 JWT 配置

JwtModule.register({
   secret: 'secretkey',
   publicKey: '...',
   privateKey: '...',
   secretOrKeyProvider: (
     requestType: JwtSecretRequestType,
     tokenOrPayload: string | Object | Buffer,
     verifyOrSignOrOptions?: jwt.VerifyOptions | jwt.SignOptions
   ) => {
     switch (requestType) {
       case JwtSecretRequestType.SIGN:
         return 'privateKey';
       case JwtSecretRequestType.VERIFY:
         return 'publicKey';
       default:
         return 'secretkey';
     }
   },
 })

未启用的 jwtconfig.ts 代码

import { JwtOptionsFactory, JwtModuleOptions } from '@nestjs/jwt'

export class JwtConfigService implements JwtOptionsFactory {
    createJwtOptions(): JwtModuleOptions {
      return {
        secret: 'secretkey'
      };
    }
  }

问题原因分析

  1. 密钥配置冲突:

    • JwtModule同时配置了secret、publicKey/privateKey和secretOrKeyProvider,配置优先级混乱。secretOrKeyProvider优先级更高,签名时实际使用字符串'privateKey'而非手动传入的'secretkey',但验证时又指定secret: 'secretkey',导致签名与验证密钥不匹配,令牌无效。
    • publicKey和privateKey使用占位符'...'而非实际密钥内容,也会导致非对称加密验证失败。
  2. 异常处理错误:

    • Guard的try-catch块中抛出普通Error而非Nest内置的HttpException子类(如UnauthorizedException),Nest会将其视为未处理异常,返回500错误而非正确的4xx状态码。
  3. Guard返回值不符合规范:

    • canActivate方法要求返回boolean或Promise<boolean>,但返回的是request.userId(字符串类型),Nest无法正确识别权限状态,引发异常。

解决方案

1. 统一密钥配置

如果不需要非对称加密,简化JwtModule配置,确保签名与验证使用同一密钥:

// UsersModule 中的 JWT 配置修改为
JwtModule.register({
  secret: 'secretkey',
  signOptions: { expiresIn: '1h' },
})

同时移除login方法中手动传入的secret参数,让模块配置生效:

// AuthService login 方法修改
const token = this.jwtService.sign(payload); // 模块已配置expiresIn,无需重复传入

若使用非对称加密,确保publicKey和privateKey是实际的密钥字符串,并移除手动传入的secret参数,依赖secretOrKeyProvider自动切换密钥:

JwtModule.register({
  publicKey: '你的实际公钥内容',
  privateKey: '你的实际私钥内容',
  secretOrKeyProvider: (
    requestType: JwtSecretRequestType,
    tokenOrPayload: string | Object | Buffer,
    verifyOrSignOrOptions?: jwt.VerifyOptions | jwt.SignOptions
  ) => {
    switch (requestType) {
      case JwtSecretRequestType.SIGN:
        return process.env.JWT_PRIVATE_KEY; // 建议用环境变量存储密钥
      case JwtSecretRequestType.VERIFY:
        return process.env.JWT_PUBLIC_KEY;
      default:
        return process.env.JWT_SECRET;
    }
  },
})

2. 修正异常处理

将Guard中的普通Error替换为Nest内置的UnauthorizedException:

// JwtAuthGuard 修改 try-catch 块
try {
  decodedToken = this.jwtService.verify(token, {secret: 'secretkey'});
} catch (err) {
  throw new UnauthorizedException('Cannot verify token.');
}

3. 修正Guard返回值

canActivate方法最后返回true,表示允许访问:

// JwtAuthGuard 最后一行修改
request.userId = decodedToken.userId;
console.log({decodedToken, token});
return true;

4. 清理冗余配置

删除未启用的JwtConfigService相关代码,或如果要使用动态配置,确保正确注册到模块中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 23:30:28