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

Posted

技术标签:

【中文标题】NestJs - 无法在 RolesGuard 中获取用户上下文【英文标题】:NestJs - Unable to get user context in RolesGuard 【发布时间】:2018-11-20 22:17:06 【问题描述】:

我使用 NestJS 作为客户端 API 的框架。在框架内,我们使用了一个运行良好的非常标准的 Passport/JWT 身份验证基础设施。当找到承载令牌时,我们的 AuthGuard 正在触发,并且在安全的 API 端点中,我可以通过“@Res() 请求”注入 HTTP 上下文并访问包含我的 Jwt 令牌有效负载的“request.user”属性.

除此之外,我们正在尝试以与文档中提供的示例代码和 GitHub 上的一些示例项目非常相似的方式实现“RolesGuard”(其中没有一个实际使用此防护,但它们将其包含为一个样本守卫)。

我们的问题是我们的 AuthGuard 触发并验证 Jwt 令牌,然后我们的 RolesGuard 触发但它传递的请求对象没有附加到请求的用户元数据。

我们 RolesGuard 中的关键代码是:

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

    if (!user) 
        return false;
    

在上面的截图中,用户总是假的。有没有人在 Nest 中编写了一个基于角色/权限的守卫,成功地访问了当前用户的范围?所有代码都在触发,并且所有内容都已正确注册。

-凯文

【问题讨论】:

【参考方案1】:

最终这似乎是守卫的排序问题,并且看起来不容易解决(没有框架允许对排序进行一些控制)。

我希望在全局范围内注册 RolesGuard,但这会导致它首先注册并首先触发。

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

如果我在端点级别注册它并将其放在 AuthGuard 之后,那么它会第二次触发,我会在防护本身中获得我期望的用户上下文。它并不完美,但它有效。

-凯文

【讨论】:

这应该被标记为答案。它工作得很好,可以明确地命令守卫。 它可以通过中间件解决并通过元数据查找方法,但我不会打扰...... 这可以使用快速会话解决吗?或者这不是一个好方法? 我发现这个答案完全可以接受,应该可以解决 99% 的用例。我假设您正在尝试将此逻辑实现为 global 防护,它将在您的 JWT 身份验证防护之前执行(假设它已在控制器或路由级别注册)。查看请求生命周期here。顺序:全局守卫 -> 控制器守卫 -> 路由守卫【参考方案2】:

在端点级别注册 RoleGuard 并将其放在 AuthGuard 之后,然后它会第二次触发,我在防护本身中获得了我期望的用户上下文。 不要在模块中注册 RoleGuard,因为它会先注册并先触发。

*.module.ts

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

【讨论】:

【参考方案3】:

如果其他人偶然发现了这个问题:将多个守卫放入一个 @UseGuards 装饰器是可行的,但如果你想让它们分开(例如,如果你使用自定义装饰器),你可以让第二个守卫访问 @ 987654323@ 将其放置在 将用户置于请求对象上的 @UseGuards 调用之前,如下例所示:

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

看来这是how decorators work in TypeScript的结果。

【讨论】:

【参考方案4】:

让你的 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();
    

换句话说,您必须先进行身份验证,然后再进行授权

【讨论】:

以上是关于NestJs - 无法在 RolesGuard 中获取用户上下文的主要内容,如果未能解决你的问题,请参考以下文章

无法窥探原始值;在nestJS中给定的未定义

无法使用应用实例方法在 NestJS 中启用 CORS

无法使用 typeorm (NestJS) 在 SQL Server 中插入 bigint

无法在所有客户端 angular/nestjs 套接字 io 中接收数据

NestJs:无法读取模块文件中的环境变量但能够读取服务文件?

NestJS - 无法初始化 GraphQL 项目