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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 01:52:53