在 Nestjs 中使用 graphql 进行授权

Posted

技术标签:

【中文标题】在 Nestjs 中使用 graphql 进行授权【英文标题】:Authorization in Nestjs using graphql 【发布时间】:2021-06-09 07:49:59 【问题描述】:

我已经开始学习 Nestjs、express 和 graphql。 我在尝试授权使用 jwt 令牌进行身份验证的用户访问时遇到问题。 我按照教程在Nestjs website 上进行身份验证。 我能够获取当前用户,但是当我尝试实现basic role base access control 时,我无法在 canActivate 方法中访问当前用户。 我认为这是因为 Roles Guard 在 Graphql Guard 之前执行。

我会在这里发布代码

gql-auth.guard.ts

import  ExecutionContext  from "@nestjs/common";
import  GqlExecutionContext  from "@nestjs/graphql";
import  AuthGuard  from "@nestjs/passport";

export class GqlAuthGuard extends AuthGuard("jwt") 
    getRequest(context: ExecutionContext) 
        const ctx = GqlExecutionContext.create(context);
        console.log("gql simple context: ", context);
        console.log("gqlContext: ", ctx.getContext());
        return ctx.getContext().req;
    

roles.guard.ts

import  CanActivate, ExecutionContext, Injectable  from "@nestjs/common";
import  Reflector  from "@nestjs/core";
import  GqlExecutionContext  from "@nestjs/graphql";

@Injectable()
export class RolesGuard implements CanActivate 
    constructor(private reflector: Reflector) 

    canActivate(context: ExecutionContext) 
        const roles = this.reflector.get<string[]>("roles", context.getHandler());
        const ctx = GqlExecutionContext.create(context);
        console.log("roles: ", roles);
        console.log("context: ", context.switchToHttp().getRequest());
        console.log("gqlContext: ", ctx.getContext().req);

        return true;
    

jwt.strategy.ts

import  Injectable  from "@nestjs/common";
import  PassportStrategy  from "@nestjs/passport";
import  ExtractJwt, Strategy  from "passport-jwt";
import  jwtConstants  from "../constants";

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) 
    constructor() 
        super(
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false,
            secretOrKey: jwtConstants.secret,
        );
    

    validate(payload: any) 
        console.log("payload: ", payload);

        return payload;
    

解析器

@UseGuards(GqlAuthGuard)
@Roles("ADMIN")
@UseGuards(RolesGuard)
@Query((returns) => [Specialty],  nullable: "itemsAndList", name: "specialties" )
async getSpecialties(@Args() params: FindManySpecialtyArgs, @Info() info: GraphQLResolveInfo) 
    const select = new PrismaSelect(info).value;
    params =  ...params, ...select ;
    return this.prismaService.specialty.findMany(params);

之前有没有人成功实现过这个?

【问题讨论】:

【参考方案1】:

你应该在同一个 @UseGuards() 装饰器中使用这两个守卫。喜欢@UseGuards(GqlAuthGuard, RolesGuard)。 Nest 将毫无问题地按顺序运行这些。

【讨论】:

嗨,为什么要使用空的“@UseGuards()”? 我提到了装饰器的全名。不要看太多【参考方案2】:
export const Authorize = (roles?: string | string[]) =>
  applyDecorators(
    SetMetadata('roles', [roles].flat()),
    UseGuards(GqlAuthGuard, RolesGuard),
  );
@Authorize("ADMIN")
@Query((returns) => [Specialty],  nullable: "itemsAndList", name: "specialties" )
async getSpecialties(@Args() params: FindManySpecialtyArgs, @Info() info: GraphQLResolveInfo) 
    const select = new PrismaSelect(info).value;
    params =  ...params, ...select ;
    return this.prismaService.specialty.findMany(params);


【讨论】:

【参考方案3】:

完整的授权码Graphql和NestJs:

@Injectable()
export class RolesGuard_ implements CanActivate 
  constructor(private reflector: Reflector) 

  canActivate(context: ExecutionContext): boolean 
    const ctx = GqlExecutionContext.create(context);

    const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    if (!requiredRoles) 
      return true;
    

    const  user  = ctx.getContext().req;
    return requiredRoles.some((role) => user.role?.includes(role));
  

【讨论】:

以上是关于在 Nestjs 中使用 graphql 进行授权的主要内容,如果未能解决你的问题,请参考以下文章

NestJS 中与 GraphQL 的 Prisma 自关系

NestJS + Mongoose + GraphQL:“填充”不起作用

nestjs 将接口转换为 graphql 类

在 Nestjs/GraphQL 中使用接口时避免循环依赖

nestjs 是不是支持 graphql 片段?

@nestjs/swagger 没有设置授权标头