NestJS中JwtAuthGuard验证失败却仍执行控制器方法问题
NestJS JwtAuthGuard验证失败但未阻止请求执行的排查思路
问题背景
接手的NestJS项目中配置了JwtAuthGuard用于校验JWT,从日志可见Guard已捕获JWT过期错误并抛出UnauthorizedException,但控制器方法仍能执行,且JwtRoleGuard输出了“用户权限验证通过”的日志,HTTP响应返回401但请求流程未被正确终止。
涉及代码
JwtAuthGuard实现
import { ExecutionContext, Injectable, UnauthorizedException, Logger, } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { IS_PUBLIC_KEY } from './decorators/public.decorator'; @Injectable() /** * An API guard used to indicate if the decorated API requires authenticaiton. If the API's class (or method) is decorated with @Public, then authentication is not required. */ export class JwtAuthGuard extends AuthGuard('jwt') { private readonly logger = new Logger(JwtAuthGuard.name); constructor(private reflector: Reflector) { super(); } // returns true if the api is @Public. Otherwise, the api will require a valid token as per the Passport strategy jwtauth.stratagy canActivate(context: ExecutionContext) { const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [ context.getHandler(), context.getClass(), ]); if (isPublic) { return true; } else { return super.canActivate(context); } } handleRequest(err, user, info) { if (err || !user) { this.logger.error(`JWT is not Valid. Err: ${err}. - User ${user}. - Info. ${info}`); throw err || new UnauthorizedException(); } return user; } }
控制器配置
@UseGuards(JwtRoleGuard) @ApiTags("complaint") @Controller({ path: 'complaint', version: '1'}) export class ComplaintController { constructor(private readonly complaintService: ComplaintService) {} @Get(':id') @Roles(Role.COS_OFFICER) findOne(@Param('id') id: string) { return this.complaintService.findOne(id); } @Patch(':id') @Roles(Role.COS_OFFICER) update(@Param('id') id: string, @Body() updateComplaintDto: UpdateComplaintDto) { return this.complaintService.update(id, updateComplaintDto); } }
日志输出
2023-10-02 17:25:28 [Backend - c1c7ed] error 2023-10-03 12:25:28.331 [JwtAuthGuard] JWT is not Valid. Err: null. - User false. - Info. TokenExpiredError: jwt expired - { stack: [ null ] } 2023-10-02 17:25:28 [Backend - c1c7ed] info 2023-10-03 12:25:28.335 [HTTP] PATCH /v1/hwcr-complaint/a5d359d1-1105-4b8e-b599-c66f3c948a56 401 - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36 Edg/116.0.1938.81 - {} 2023-10-02 17:25:28 [Backend - c1c7ed] debug 2023-10-03 12:25:28.397 [JwtRoleGuard] Guarded Roles: COS Officer - {} 2023-10-02 17:25:28 [Backend - c1c7ed] debug 2023-10-03 12:25:28.397 [JwtRoleGuard] User authorization verified - {}
排查思路
1. 确认Guard的注册与执行顺序
- 检查JwtAuthGuard的注册方式:如果是通过
APP_GUARD令牌全局注册,全局Guard会先于控制器上的JwtRoleGuard执行。日志显示JwtAuthGuard抛出异常后JwtRoleGuard仍在执行,说明异常未中断请求流程,可能是JwtAuthGuard的异常被错误捕获,或是JwtRoleGuard未处理前置验证结果。 - 核实JwtRoleGuard是否依赖JwtAuthGuard的验证结果:比如是否从
request.user获取用户信息,如果没有判断request.user是否存在就直接通过验证,会导致JWT无效时仍能通过角色校验。
2. 检查JwtRoleGuard的核心逻辑
重点查看JwtRoleGuard的canActivate方法:
- 是否存在
if (!request.user) throw new UnauthorizedException()的判断?如果缺失,即使JwtAuthGuard抛出异常,JwtRoleGuard可能跳过用户有效性检查直接通过。 - 是否错误使用
try/catch捕获异常后返回true?比如捕获异常后未重新抛出,反而返回true,导致请求继续执行。
3. 验证JwtAuthGuard的异常抛出逻辑
- 在
handleRequest方法中增加日志,打印user的具体类型和值,确认Passport策略返回的user是否符合预期(比如是否为false或null)。 - 确认
super.canActivate(context)的异步处理:super.canActivate返回Promise<boolean>,如果未用async/await处理异步逻辑,可能导致异常未及时抛出,后续Guard继续执行。
4. 排查全局异常过滤器或拦截器
- 检查是否存在自定义全局异常过滤器:如果过滤器错误捕获
UnauthorizedException并修改响应,但未终止请求流程,会导致后续Guard继续执行。 - 查看是否有拦截器在Guard之后执行:某些拦截器可能错误恢复请求流程,导致控制器方法执行。
5. 确认控制器方法是否真的执行
- 在控制器的
update和findOne方法中添加日志(如logger.log('update method triggered')),验证方法是否实际执行。日志中的HTTP 401可能是异常过滤器返回的,而控制器方法并未执行,需区分这两种情况。
内容的提问来源于stack exchange,提问作者Marqueone
相关产品推荐
相关产品推荐

