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

NestJS如何在Multipart/Form Data中实现角色权限校验?

问题原因

核心问题是NestJS的守卫执行优先级高于Multer拦截器:

  • 普通JSON请求的body由Nest默认的body-parser中间件提前解析,守卫能正常拿到request.body;
  • 但Multipart/Form Data需要FileInterceptor解析,而拦截器是在守卫之后、控制器方法执行前运行的。所以当RolesGuard执行时,Form Data的请求体还没被解析,body.role自然是undefined。
解决方案

推荐两种方案,优先选更安全的权限校验逻辑:

方案一:基于登录用户角色校验(更合理)

你的接口中已经通过@GetUser()获取了登录用户信息,说明AuthGuard已经将用户数据注入到request.user中。正常权限校验应该基于登录用户的角色,而非请求体传递的role(避免篡改风险)。

修改RolesGuard代码:

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Role } from '../enums/role.enum';
import { ROLES_KEY } from '../decorators/roles.decorator';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!requiredRoles) {
      return true;
    }
    
    // 从AuthGuard解析后的user对象中取角色,不受请求体格式影响
    const { user } = context.switchToHttp().getRequest();
    return requiredRoles.some((role) => user.role?.includes(role));
  }
}

方案二:提前解析Form Data(必须从请求体拿role时用)

通过配置Multer中间件,让Form Data在守卫执行前就完成解析:

1. 在模块中配置Multer中间件

比如在你的CandidatesModule中实现NestModule接口:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { MulterModule, Multer } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
import { extname } from 'path';
import { CandidatesController } from './candidates.controller';
import { CandidatesService } from './candidates.service';

@Module({
  imports: [
    MulterModule.register({
      storage: diskStorage({
        destination: './uploads',
        filename: (req, file, cb) => {
          const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
          cb(null, `${file.fieldname}-${uniqueSuffix}${extname(file.originalname)}`);
        },
      }),
    }),
  ],
  controllers: [CandidatesController],
  providers: [CandidatesService],
})
export class CandidatesModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    // 针对目标路由应用Multer中间件,指定文件字段名
    consumer
      .apply(MulterModule.createMiddleware({ dest: './uploads' }))
      .forRoutes('candidates'); // 替换为你的实际接口路径
  }
}

2. 调整控制器代码

去掉@UseInterceptors(FileInterceptor('document'))(中间件已完成文件解析),保留其他参数:

@Post()
@Roles(Role.Admin)
@UseGuards(AuthGuard())
async create(
  @UploadedFile() document: Express.Multer.File,
  @Body() createCandidats: CreateCandidats,
  @GetUser() user: User,
  @Body('role') role: string
): Promise<Candidate> {
  // 原有业务逻辑不变
  const documentName = document.filename;
  createCandidats.documents = documentName;
  return await this.candidateService.create(createCandidats, user);
}

此时RolesGuard中的request.body已经被Multer解析,能正常拿到role参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:07:51