NestJS中如何让自定义AuthGuard优先于RolesGuard执行?
解决NestJS中AuthGuard与全局RolesGuard执行顺序问题
问题根源
NestJS的全局守卫会优先于控制器/方法级的局部守卫执行。你把RolesGuard注册为全局守卫后,它会在BookController上的局部AuthGuard之前运行,此时request对象还未被挂载用户信息,导致角色校验失败。
下面是三种可行的解决方案:
方案1:将AuthGuard也注册为全局守卫,控制执行顺序
把AuthGuard和RolesGuard都设为全局守卫,注册时先传入AuthGuard,再传入RolesGuard——useGlobalGuards的参数顺序就是守卫的执行顺序,先传的先执行。
代码示例(main.ts)
async function bootstrap() { const app = await NestFactory.create(AppModule); // 先执行AuthGuard认证,再执行RolesGuard授权 app.useGlobalGuards(new AuthGuard(), new RolesGuard()); await app.listen(3000); } bootstrap();
或通过AppModule的APP_GUARD提供者注册
// app.module.ts import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; import { AuthGuard } from './auth/auth.guard'; import { RolesGuard } from './auth/roles.guard'; @Module({ providers: [ // 先注册的守卫先执行 { provide: APP_GUARD, useClass: AuthGuard }, { provide: APP_GUARD, useClass: RolesGuard }, ], }) export class AppModule {}
这种方式下,所有接口都会先完成JWT认证、挂载用户信息,再进行角色校验,同时保留@Roles(Role.USER)装饰器指定接口所需角色即可。
方案2:移除RolesGuard的全局注册,在方法级结合AuthGuard使用
如果不需要RolesGuard作用于所有接口,可在需要授权的方法上同时指定两个守卫,把AuthGuard放在RolesGuard前面——@UseGuards的参数顺序决定执行顺序。
代码示例(BookController)
// book.controller.ts import { Controller, Get, UseGuards } from '@nestjs/common'; import { AuthGuard } from '../auth/auth.guard'; import { RolesGuard } from '../auth/roles.guard'; import { Roles } from '../auth/roles.decorator'; import { Role } from '../auth/role.enum'; @Controller('books') export class BookController { @Get() // 先执行AuthGuard认证,再执行RolesGuard授权 @UseGuards(AuthGuard, RolesGuard) @Roles(Role.USER) getAllBooks() { // 业务逻辑实现 } }
记得在AppModule中移除RolesGuard的全局注册配置。
方案3:让RolesGuard依赖AuthGuard的执行结果(进阶)
如果必须保留RolesGuard的全局注册,可修改RolesGuard,在角色校验前先触发AuthGuard的认证逻辑,确保用户信息已挂载。
代码示例(修改后的RolesGuard)
// roles.guard.ts import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AuthGuard } from './auth.guard'; import { Role } from './role.enum'; @Injectable() export class RolesGuard implements CanActivate { constructor( private reflector: Reflector, private authGuard: AuthGuard, // 注入AuthGuard实例 ) {} async canActivate(context: ExecutionContext): Promise<boolean> { // 先完成认证 const isAuthPassed = await this.authGuard.canActivate(context); if (!isAuthPassed) { throw new UnauthorizedException('请先完成认证'); } // 再执行角色校验 const requiredRoles = this.reflector.getAllAndOverride<Role[]>('roles', [ context.getHandler(), context.getClass(), ]); if (!requiredRoles) return true; const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } }
注意:这种方式会增加两个守卫的耦合度,仅适用于必须保留RolesGuard全局注册的场景。
内容的提问来源于stack exchange,提问作者Happy Patel
相关产品推荐
相关产品推荐

