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



我正在使用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 中编写了基于角色/权限的防护,成功访问当前用户的范围? 所有代码都在触发,并且所有内容都已正确注册。

-凯文

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

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

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

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

-凯文

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

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

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

*.module.ts

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

如果其他人偶然发现了这个问题:将多个守卫放入一个@UseGuards装饰器中是有效的,但如果你想将它们分开(比如,如果你使用自定义装饰器(,你可以通过将其放在将用户置于请求对象的@UseGuards调用之前,为第二个守卫提供对req.user的访问权限, 如本例所示:

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

这似乎是装饰器在 TypeScript 中工作方式的结果。

您还可以使用多个角色进行基于角色的身份验证。

在用户控制器中

import {
Controller,
Get,
Post,
UseGuards,
} from '@nestjs/common';
import { UseGuards } from '@nestjs/common';
import { RolesGuard } from 'src/guards/auth.guard';
@UseGuards(new RolesGuard(['admin']))
@Controller()
export class UserController { ... }

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;
}
}

最新更新