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

NestJS RoleGuard中user.roles未定义引发403权限错误求助

问题根源分析与解决方案

从你提供的代码来看,导致user.roles为undefined进而触发403的核心原因有两个:

1. JWT策略的validate方法未返回用户角色信息

你的JwtStrategy里的validate方法只返回了userId和email,完全没有包含roles字段:

async validate(payload: any) {
  return { userId: payload.sub, email: payload.email };
}

NestJS会把这个方法的返回值挂载到请求的user对象上,所以req.user自然没有roles属性。

解决方法:修改JWT策略,从数据库查询完整用户信息(包含角色)

首先注入用户模型,然后在validate方法中根据JWT payload里的sub(用户ID)查询数据库,返回包含roles的用户对象:

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { jwtConstants } from './constants';
import { InjectModel } from '@nestjs/mongoose';
import { UserEntity, UserDocument } from 'src/user/user.entity'; // 路径根据你的项目结构调整

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    @InjectModel(UserEntity.name) private userModel: Model<UserDocument>,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.secret,
    });
  }

  async validate(payload: any) {
    // 根据用户ID查询数据库,获取完整用户信息,排除敏感的password字段
    const user = await this.userModel.findById(payload.sub).select('-password');
    // 返回包含roles的用户对象
    return { 
      userId: user._id, 
      email: user.email, 
      roles: user.roles 
    };
  }
}

2. 用户实体的roles字段Mongoose配置错误

你的UserEntity中roles字段的@Prop配置有问题:

@Prop({ required: true, type: String, enum: Role, default: Role.BUYER })
roles: Role[];

你声明的属性类型是Role[](数组),但Mongoose的type设置为String,这会导致数据库中roles被存储为单个字符串,而不是数组。后续即使查询到用户,user.roles也可能是字符串而非数组,导致includes方法失效。

解决方法:修正Mongoose的字段配置

把type改为数组类型[String],同时默认值也改为数组:

@Prop({ required: true, type: [String], enum: Role, default: [Role.BUYER] })
roles: Role[];

额外建议:确保JWT生成时可选携带角色(非必须,但可优化)

如果你希望JWT payload中直接包含角色信息(减少数据库查询),可以在生成token时把roles加入payload:

// 在AuthService的login方法中
async login(user: UserDocument) {
  const payload = { 
    email: user.email, 
    sub: user._id, 
    roles: user.roles 
  };
  return {
    access_token: this.jwtService.sign(payload),
  };
}

不过更推荐从数据库查询角色,因为用户角色可能在token有效期内发生变化,数据库查询能保证获取最新的角色信息。

完成以上修改后,再调用带有@Roles(Role.BUYER)的路由,req.user.roles就会正确存在,RolesGuard也能正常判断用户角色,不会再返回403错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 19:57:50