Nest.JS 使用 AuthGuard 作为 GraphQL 的中间件

问题描述 投票:0回答:3

我正在尝试使用 PassportJS 确保我的 GraphQL 端点的安全,以便对该端点的每次调用都使用 AuthGuard 来验证令牌并在 request.user 上设置用户,就像在具有以下代码的控制器中一样:

@Get('findAll')
@UseGuards(AuthGuard('jwt'))
findAll(@Req() request): Promise<Array<Thing>> {
    return this.thingService.findByUser(request.user.email);
}

问题是我想在 graphQL 端点中使用它,它是这样创建的:

consumer
    .apply(graphiqlExpress({ endpointURL: '/graphql' }))
    .forRoutes('/graphiql')
    .apply(
        graphqlExpress(req => ({ schema, rootValue: req })),
        ¿?,
    )
    .forRoutes('/graphql');

我想我可以在 graphqlExpress 函数之后将其设置为中间件函数,但我没有成功。有什么想法吗?

提前谢谢您!

编辑

作为一种解决方法,我已经实现了 Nest Docs 上提出的解决方案,其中它在每个必须受到保护的查询/突变中使用 @UseGuard。

但是,我想保护整个端点,这样就不会为每个受保护的解析器调用守卫,而只在主请求上调用一次。这可能吗?

passport.js graphql nestjs
3个回答
2
投票

这在技术上是可行的,但写起来相当草率,而且绝对不能保证它能与 Fastify 一起工作,所以请注意。功能的核心来自于实现中间件的模块。我最终用

AppModule
完成了这一切,我不建议这样做(至少不是那里的代码的 all),但它仍然有效。

您需要使守卫成为自定义提供程序,以便可以将其注入到任何上下文中。

然后您需要使用

ExecutionContext
模拟
req, res, next
。如果您想要类型安全,这说起来容易做起来难,但如果您不关心这一点(我不关心这一点),那么就拍打
as any
并收工。

之后,在中间件使用者中,您运行

apply
并将
this.guard.canActivate
与您创建的模拟
ExecutionContext
一起使用。使这个中间件
async
await
成为
canActivate
调用。检查它是否以
true
的形式返回,如果没有,则
throw new <ErrorOfYourChoice>()
和繁荣。已经设置好了代码看起来(隐约)像这样:

import {
  BadRequestException,
  CanActivate,
  Inject,
  MiddlewareConsumer,
  Module,
  NestModule,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { AppResolver } from './app.resolver';
import { GraphQLModule } from '@nestjs/graphql';
import { JwtModule } from '@nestjs/jwt';
import { AuthGuard, PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
    }),
    JwtModule.register({ secret: 'secret' }),
    PassportModule.register({ defaultStrategy: 'jwt' }),
  ],
  controllers: [AppController],
  providers: [
    AppService,
    AppResolver,
    JwtStrategy,
    { provide: 'CustomGuard', useClass: AuthGuard() },
  ],
})
export class AppModule implements NestModule {
  constructor(@Inject('CustomGuard') private readonly guard: CanActivate) {}

  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(async (req, res, next) => {
        const canActivate = await this.guard.canActivate({
          switchToHttp: () => ({
            getRequest: () => req,
            getResponse: () => res,
            getNext: () => next,
          }),
        } as any);
        if (canActivate) {
          next();
        } else {
          throw new BadRequestException();
        }
      })
      .forRoutes('graphql');
  }
}

您可以检查此存储库以了解所有已连接和工作的内容。使用

POST /login -d 'username=test1&password=changeme'
登录,获取 JWT 并随心所欲地使用它。


0
投票

参见:https://docs.nestjs.com/graphql/other-features#execution-context

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const ctx = GqlExecutionContext.create(context);
    return true;
  }
}

-1
投票

但是,我想保护整个端点,这样就不会为每个受保护的解析器调用守卫,而只在主请求上调用一次。这可能吗?

通过使用 NestJS 的参考全局方法,我能够获得一个中间件函数来解析每个查询/突变:https://docs.nestjs.com/graphql/field-middleware#global-field-middleware

© www.soinside.com 2019 - 2024. All rights reserved.