使用 graphql 在 Nestjs 中授权

Authorization in Nestjs using graphql

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

我会post这里的代码

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

有没有人成功实施过这个?

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

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

完成授权码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));
  }
}