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

Rob*_*ual 7 passport.js graphql nestjs

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

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

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

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

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

先感谢您!

编辑

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

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

Jay*_*iel 2

这在技术上是可行的,但写起来相当草率,而且绝对不能保证它能与 Fastify 一起工作,所以请注意。功能的核心来自于实现中间件的模块。我最终用我不建议的方式完成了这一切AppModule(至少不是那里的所有代码),但它仍然有效。

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

然后你需要模拟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');
  }
}
Run Code Online (Sandbox Code Playgroud)

您可以检查此存储库以了解所有已连接并正常工作的内容。使用 登录POST /login -d 'username=test1&password=changeme',获取 JWT 并随心所欲地使用它。