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

NestJS实现RBAC时遇TypeError: Cannot read properties of undefined (reading 'roles')

NestJS RBAC 报错「TypeError: Cannot read properties of undefined (reading 'roles')」排查修复

问题描述

在NestJS中实现基于角色的访问控制(RBAC)时,持续遭遇错误:

TypeError: Cannot read properties of undefined (reading 'roles')

已参照官方文档和教程尝试解决,耗时两周仍未解决,恳请协助排查修复。

排查与修复方案

以下是针对该错误的常见原因及修复步骤:

1. 验证role.decorator.ts的元数据配置

确保装饰器正确使用SetMetadata挂载角色元数据,且常量键一致:

import { SetMetadata } from '@nestjs/common';

export const ROLES_KEY = 'roles'; // 注意这个键要和Guard中使用的完全一致
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
  • 常见错误:ROLES_KEY拼写错误,导致Guard无法读取到元数据。

2. 修正role.guard.ts的逻辑

Guard中必须通过Reflector获取元数据,且先校验用户是否存在:

import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './role.decorator';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {} // 必须注入Reflector

  canActivate(context: ExecutionContext): boolean {
    // 获取路由/控制器上的角色要求
    const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requiredRoles) return true; // 无角色要求则直接放行

    // 获取请求对象中的用户信息(需先由AuthGuard挂载)
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    // 先判断用户是否存在
    if (!user) {
      throw new UnauthorizedException('请先登录');
    }

    // 校验用户角色是否匹配
    return requiredRoles.some(role => user.roles?.includes(role));
  }
}
  • 常见错误:
    • 未注入Reflector导致无法读取元数据;
    • 未先判断user是否存在就直接访问user.roles;
    • 上下文转换错误(比如非HTTP场景误用switchToHttp())。

3. 检查auth.controller.ts的装饰器与Guard绑定

确保@Roles()装饰器正确应用,且RolesGuard在AuthGuard之后执行:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { Roles } from './role.decorator';
import { RolesGuard } from './role.guard';
import { AuthGuard } from '@nestjs/passport';

@Controller('auth')
@UseGuards(AuthGuard('jwt')) // 先通过AuthGuard挂载user到request对象
export class AuthController {
  @Get('admin-only')
  @Roles('admin') // 必须添加在路由方法或控制器上
  @UseGuards(RolesGuard) // 局部绑定Guard(全局注册则无需此步)
  getAdminResource() {
    return { data: '仅管理员可见内容' };
  }
}
  • 常见错误:
    • @Roles()装饰器未正确添加到目标路由/控制器;
    • RolesGuard在AuthGuard之前执行,导致request.user未被初始化。

4. 确认user.service.ts的用户数据返回

确保用户验证逻辑返回的对象包含roles字段:

import { Injectable } from '@nestjs/common';

@Injectable()
export class UserService {
  async validateUser(username: string): Promise<any> {
    // 从数据库或其他数据源获取用户信息
    const user = await this.userRepository.findOne({ where: { username } });
    if (!user) return null;

    // 确保返回的用户对象包含roles字段
    const { password, ...safeUser } = user;
    return { ...safeUser, roles: user.roles }; // 或直接返回包含roles的对象
  }
}
  • 常见错误:用户对象缺少roles字段,或字段名拼写错误(如role而非roles)。

5. 全局Guard注册的正确性

如果全局注册RolesGuard,需在模块中正确配置:

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { RolesGuard } from './role.guard';
import { AuthGuard } from '@nestjs/passport';

@Module({
  providers: [
    { provide: APP_GUARD, useClass: AuthGuard('jwt') }, // 全局注册AuthGuard确保user被挂载
    { provide: APP_GUARD, useClass: RolesGuard },
  ],
})
export class AppModule {}
  • 注意:全局Guard的执行顺序与注册顺序一致,需确保AuthGuard先于RolesGuard注册。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 17:17:47