Nestjs Roles Decorator:让用户脱离有效负载数据的疯狂行为

Posted

技术标签:

【中文标题】Nestjs Roles Decorator:让用户脱离有效负载数据的疯狂行为【英文标题】:Nestjs Roles Decorator: Crazy behaviour with getting user out of payload data 【发布时间】:2022-01-03 23:52:33 【问题描述】:

我只是想在 Nestjs 中实现我的角色装饰器。到目前为止一切顺利,直到我想将来自 jwt-token 的有效负载数据的用户与所需的角色进行比较。我无法解释这是怎么可能的,但只有让函数始终返回 true,才能从有效负载数据中获取用户和角色。在我设置条件后,用户未定义。这怎么可能?

这是我的(缩短的)用户控制器,我在其中使用装饰器:

    @UseGuards(JwtAuthGuard, RolesGuard)
@Controller('users')
export class UsersController 
  constructor(private readonly userService: UserService) 
  private readonly logger = new Logger(LoggingService.name);

  @Post('/create')
  async create(@Body() createUserDto: CreateUserDto) 
    const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
    createUserDto.password = hashedPassword
    return this.userService.createUser(createUserDto);
  

  @Roles(Role.SUPERADMIN)
  @Get('/')
  showUsers() 
    return this.userService.getUsers();
  

这里是角色装饰器,有条件:

import  CanActivate, ExecutionContext, Injectable, Logger  from '@nestjs/common';
import  Reflector  from '@nestjs/core';
import  Observable  from 'rxjs';
import  LoggingService  from 'src/services/logging/logging.service';
import  User  from 'src/user/schemas/user.schema';
import  Role  from '../role.enum';
import  ROLES_KEY  from '../decorators/roles.decorator';
import  Types  from 'mongoose';

@Injectable()
export class RolesGuard implements CanActivate 
  constructor(private readonly reflector: Reflector) 
  private readonly logger = new Logger(LoggingService.name);

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

        const roles = this.reflector.get<Role[]>(ROLES_KEY, context.getHandler());
        // If there is no Roles-Decorator, just pass through
        if (!roles) 
          return true;
        

        this.logger.debug("REQUIRED ROLES: ", roles)
        

        const request = context.switchToHttp().getRequest();
        const userRole = request.user?.roles;
        const userID = request.user?.sub;
        this.logger.debug("ROLES GUARD USER", userID);
        this.logger.debug("USER ROLE", userRole);


        // Else, check the request header if it matches

        if (roles.includes(userRole)) 
            return true;
         else  return false 

  


记录输出(当我尝试访问路由时):

[Nest] 4460  - 25.11.2021, 18:56:33   DEBUG [LoggingService] REQUIRED ROLES: 
[Nest] 4460  - 25.11.2021, 18:56:33   DEBUG [LoggingService] superadmin
[Nest] 4460  - 25.11.2021, 18:56:33   DEBUG [LoggingService] ROLES GUARD USER
[Nest] 4460  - 25.11.2021, 18:56:33   DEBUG [LoggingService] undefined
[Nest] 4460  - 25.11.2021, 18:56:33   DEBUG [LoggingService] USER ROLE
[Nest] 4460  - 25.11.2021, 18:56:33   DEBUG [LoggingService] undefined

但是当我这样做时:

// Else, check the request header if it matches

        // if (roles.includes(userRole)) 
        //     return true;
        //  else  return false 

        return true;

日志输出是这样的:

[Nest] 39888  - 25.11.2021, 19:00:14   DEBUG [LoggingService] REQUIRED ROLES: 
[Nest] 39888  - 25.11.2021, 19:00:14   DEBUG [LoggingService] superadmin
[Nest] 39888  - 25.11.2021, 19:00:14   DEBUG [LoggingService] ROLES GUARD USER
[Nest] 39888  - 25.11.2021, 19:00:14   DEBUG [LoggingService] 619962ad86e412dc06983a0e
[Nest] 39888  - 25.11.2021, 19:00:14   DEBUG [LoggingService] USER ROLE
[Nest] 39888  - 25.11.2021, 19:00:14   DEBUG [LoggingService] superadmin

控制流是从上到下的,对吧?任何帮助将不胜感激!

【问题讨论】:

您的应用程序中是否恰好有一个提供程序anywhere,例如 provide: APP_GUARD, useClass: RolesGuard 是的,我在 users.module 中提供:@Module( imports: [MongooseModule.forFeature([ name: User.name, schema: UserSchema ])], controllers: [UsersController], providers: [UserService, UserRepository, LoggingService, provide: APP_GUARD, useClass: RolesGuard, ], exports: [UserService] ) export class UsersModule 【参考方案1】:

在应用程序注册的 any 模块中使用APP_GUARD 会将守卫注册为全局守卫,它将在每个请求之前运行。我要做的是注册两个全局守卫,一个用于JwtAuthGuard 一个用于RolesGuard,并实现一个@JwtSkip() 装饰器,它告诉JwtAuthGuard 它可以跳过这条路由上的身份验证(将是相同的对于RolesGuard,因为它不应该有任何与之关联的角色

【讨论】:

感谢您的回复!按照这个逻辑,我只是​​尝试删除 JwthAuthguard 并仅将 RolesGuard 用于 users.controller。但仍然是相同的输出:[Nest] 43020 - 25.11.2021, 21:53:19 DEBUG [LoggingService] REQUIRED ROLES: [Nest] 43020 - 25.11.2021, 21:53:19 DEBUG [LoggingService] superadmin [Nest] 43020 - 25.11.2021, 21:53:19 DEBUG [LoggingService] ROLES GUARD USER [Nest] 43020 - 25.11.2021, 21:53:19 DEBUG [LoggingService] undefined JwtAuthGuard 设置了req.user,因为passport 设置了req.user。否则,没有user 作为请求对象的一部分,这就是为什么我建议将JwtAuthGuard 设为全局并确保它在RolesGuard 之前运行 谢谢,我根据您的输入找到了解决方案。在下面检查我的答案!【参考方案2】:

我找到了解决方案!

我没有在全局上下文中注册 RolesGuard。所以我继续放

app.useGlobalGuards(new RolesGuard(new Reflector()));

在 main.ts 中。 然后它突然起作用了。我修改了通过Guard的条件如下:

// Else, check the request header if it matches

        if (roles.some(r => r == userRole)) 
            this.logger.log("ROLES GUARD: PASS")
            return true;
         else  
          this.logger.error("ROLES GUARD: DENIED")
          return false 
        

希望这对任何人都有帮助!

【讨论】:

以上是关于Nestjs Roles Decorator:让用户脱离有效负载数据的疯狂行为的主要内容,如果未能解决你的问题,请参考以下文章

NestJS 自定义装饰器返回未定义

NestJS - 如何使用 @Body() 装饰器访问帖子正文?

如何在不是nestjs路由的方法上设置装饰器

如何在@nestjs/mongoose 模式中设置枚举

如何使用 @Transaction() 装饰器为方法编写单元测试?

结合使用allure当中的方法 让用例执行结果内 显示详细描述信息