1

我正在尝试在 Nest JS API 中添加角色保护。我为此使用了 Passport、Jwt 身份验证。在我的 RolesGuard 类中,我提出了请求并从中获取用户以检查用户角色是否有效。我附上了下面的代码。

角色.guard.ts

canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {

  const roles = this.reflector.get<string[]>('roles', context.getHandler());

  if (!roles) {
    return true;
  }

  const request = context.switchToHttp().getRequest();
  const user: User = request.user;

  return this.userService.findOne(user.id).pipe(
    map((user: User) => {
      const hasRole = () => roles.indexOf(user.role) > -1;
      let hasPermission: boolean = false;

      if (hasRole()) {
        hasPermission = true;
      }
      return user && hasPermission;
    }),
  );

}

这里的问题是 context.switchToHttp().getRequest() 返回对象,这是未定义的。所以我无法从中获取用户详细信息。在对这个错误进行一些研究后,我发现控制器中装饰器的顺序可能是问题所在。然后我更改了顺序,但问题仍然出现。贝娄我也添加了该代码。

用户控制器.ts

@UseGuards(JwtAuthGuard, RolesGuard)
@hasRoles(UserRole.USER)
@Get()
findAll(): Observable<User[]> {
  return this.userService.findAll();
}

-谢谢-

4

1 回答 1

0

如果您使用的是 graphql,您可以进行更改以适应下面的代码

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
import { Role } from 'src/enums/role.enum';
import { ROLES_KEY } from '../auth';
@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;
        }
        const ctx = GqlExecutionContext.create(context);
        const user = ctx.getContext().req.user;
        return requiredRoles.some((role) => user.roles?.includes(role));
    }
}

如果您不使用 graphql,请进行更改以适合下面的代码

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@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;
    }
    const { user } = context.switchToHttp().getRequest();
    return requiredRoles.some((role) => user.roles?.includes(role));
  }
}

然后在你的控制器中你可以做

@UseGuards(JwtAuthGuard, RolesGuard)
@hasRoles(UserRole.USER)
@Get()
findAll(): Observable<User[]> {
  return this.userService.findAll();
}

最后,为了让这一切正常工作,像这样将全局守卫添加到您的 app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import {MongooseModule} from '@nestjs/mongoose';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import configuration from 'src/config/configuration';
import { RolesGuard } from 'src/auth/auth';
import { UsersModule } from '../users/users.module';
import { AdminsModule } from 'src/admin/admins.module';
import { config } from 'dotenv';
import * as Joi from 'joi';

config();

@Module({
  imports: [
    // other modules
    UsersModule,

    // configuration module
    ConfigModule.forRoot({
      isGlobal: true,
      cache: true,
      load: [configuration],
      expandVariables: true,
      // validate stuff with Joi
      validationSchema: Joi.object({
        NODE_ENV: Joi.string()
          .valid('development', 'production', 'test', 'provision')
          .default('development'),
        PORT: Joi.number().default(5000),
      }),
      validationOptions: {
        // allow unknown keys (change to false to fail on unknown keys)
        allowUnknown: true,
        abortEarly: true,
      },
    }),
    // connect to mongodb database
    MongooseModule.forRoot(process.env.DB_URL, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    }),
  ],

  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: 'APP_GUARD',
      useClass: RolesGuard,
    }
  ],
})

export class AppModule {}
于 2022-02-04T01:11:10.527 回答