简体   繁体   English

NestJs - 无法在 RolesGuard 中获取用户上下文

[英]NestJs - Unable to get user context in RolesGuard

I'm using NestJS as the framework for a client API.我使用 NestJS 作为客户端 API 的框架。 Within the framework we are using a pretty standard Passport/JWT auth infrastructure that is working fine.在框架内,我们使用了一个运行良好的非常标准的 Passport/JWT 身份验证基础设施。 Our AuthGuard is firing when the bearer token is found and, in secure API endpoints, I can inject the HTTP context via '@Res() request' and get access to the 'request.user' property which contains the payload of my Jwt token.当找到承载令牌时,我们的 AuthGuard 正在触发,并且在安全的 API 端点中,我可以通过“@Res() 请求”注入 HTTP 上下文并访问包含我的 Z794349 令牌的有效负载的“request.user”属性.

On top of this we are attempting to implement a 'RolesGuard' in a very similar fashion to the sample code provided in the documentation and some of the sample projects on GitHub (none of which actually use this guard but they include it as a sample guard).最重要的是,我们正在尝试以与文档中提供的示例代码和 GitHub 上的一些示例项目非常相似的方式实现“RolesGuard”(其中没有一个实际使用此防护,但它们将其包含为示例防护)。

Our issue is that our AuthGuard fires and validates the Jwt token and THEN our RolesGuard fires but the request object it is passed does not have the user meta-data attached to the request.我们的问题是,我们的 AuthGuard 触发并验证 Jwt 令牌,然后我们的 RolesGuard 触发,但它传递的请求 object 没有附加到请求的用户元数据。

The key code in our RolesGuard is:我们 RolesGuard 中的关键代码是:

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

    if (!user) {
        return false;
    }

In the above snipped the user is always false.在上面的截图中,用户总是假的。 Has anyone written a role/permission based guard in Nest that successfully gets access to the scope of the current user?是否有人在 Nest 中编写了基于角色/权限的守卫,成功访问了当前用户的 scope? All the code is firing and everything appears registered correctly.所有代码都在触发,并且所有内容都显示正确注册。

-Kevin -凯文

Ultimately this appears to be an ordering issue with the guards and it doesn't look like it can be easily resolved (without the framework allowing some control over the ordering).最终,这似乎是守卫的排序问题,而且看起来不容易解决(没有框架允许对排序进行一些控制)。

My hope was to register the RolesGuard globally but that causes it to be registered first and fire first.我的希望是全局注册 RolesGuard,但这会导致它首先注册并首先触发。

@UseGuards(AuthGuard('jwt'), RolesGuard)
@Roles('admin')

If I register it at the endpoint level and put it after the AuthGuard then it fires second and I get the user context I am expecting within the guard itself.如果我在端点级别注册它并将其放在 AuthGuard 之后,那么它会第二次触发,并且我会在警卫本身中获得我期望的用户上下文。 It isn't perfect but it works.它并不完美,但它有效。

-Kevin -凯文

register RoleGuard at the endpoint level and put it after the AuthGuard then it fires second and I get the user context I am expecting within the guard itself.在端点级别注册 RoleGuard 并将其放在 AuthGuard 之后,然后它会触发第二次,我会在警卫本身中获得我期望的用户上下文。 don't register RoleGuard at module causes it'll be registered first and fire first.不要在模块中注册 RoleGuard 会导致它首先注册并首先触发。

*.module.ts *.module.ts

imports: [],
  providers: [{provide: APP_GUARD, useClass: RolesGuard} ,],  // remove guard
  controllers: [],
  exports: [],

Make your RolesGuard extend AuthGuard('StrategyName') and then call super.canActivate for example:让您的 RolesGuard 扩展 AuthGuard('StrategyName') 然后调用 super.canActivate 例如:

@Injectable()
export class RolesGuard extends AuthGuard('jwt') {
    async canActivate(context: ExecutionContext): Promise<boolean> {

        // call AuthGuard in order to ensure user is injected in request
        const baseGuardResult = await super.canActivate(context);
        if(!baseGuardResult){
            // unsuccessful authentication return false
            return false;
        }

        // successfull authentication, user is injected
        const {user} = context.switchToHttp().getRequest();
    }
}

In other words you have to Authenticate first then Authorize换句话说,您必须先进行身份验证,然后再进行授权

If anyone else stumbles across this question: putting multiple guards into one @UseGuards decorator works, but if you want to keep them separated (say, if you use a custom decorator), you can give the 2nd guard access to req.user by placing it before the @UseGuards call that puts the user on the request object, as in this example:如果其他人偶然发现了这个问题:将多个守卫放入一个@UseGuards装饰器可以工作,但是如果您想将它们分开(例如,如果您使用自定义装饰器),您可以通过放置让第二个守卫访问req.user将用户置于请求对象上的@UseGuards调用之前,如下例所示:

@RestrictTo(UserAuthorities.admin)
@UseGuards(JwtAuthGuard)
@Get("/your-route")

It seems that this is a consequence of how decorators work in TypeScript .这似乎是装饰器在 TypeScript 中工作方式的结果。

You can also use multiple roles for role-based Authentication.您还可以使用多个角色进行基于角色的身份验证。

In UserResolver在用户解析器中

import { Args, Mutation, Query, Resolver } from '@nestjs/graphql';
import { UseGuards } from '@nestjs/common';
import { RolesGuard } from 'src/guards/auth.guard';

@UseGuards(new RolesGuard(['admin']))
@Resolver()
export class UserResolver { ... }

In RolesGuard在角色卫士中

import { ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class RolesGuard extends AuthGuard('jwt') {
  constructor(private roles: string[] | null) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    const { req } = ctx.getContext();
    return super.canActivate(new ExecutionContextHost([req]));
  }

  handleRequest(err: any, user: any, info: string) {
    if (!this.roles) {
      return user || null;
    }

    if (!user) {
      throw new UnauthorizedException('Not Valid User.');
    }

    const role = user.role;
    const doesRoleMatch = this.roles.some(r => r === role);
    if (!doesRoleMatch) {
      throw new UnauthorizedException('Not Valid User.');
    }
    return user;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM