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

Posted

技术标签:

【中文标题】Nest.JS 使用 AuthGuard 作为 GraphQL 的中间件【英文标题】:Nest.JS use AuthGuard as Middleware for GraphQL 【发布时间】:2019-01-27 09:54:53 【问题描述】:

我正在尝试使用 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。

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

【问题讨论】:

如果您仍然像在 Nest 中的 REST 应用程序中一样使用模块,您可以将 Guard 注册为提供程序 - 请参阅此处的示例以了解整个应用程序 docs.nestjs.com/guards#role-based-authentication 。这能解决你的问题吗? 【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

您需要将守卫设为custom provider,以便可以将其注入到任何上下文中。

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

之后,在中间件使用者中运行 apply 并使用 this.guard.canActivate 和您创建的模拟 ExecutionContext。将此中间件 asyncawait 设为 canActivate 调用。检查它是否以true 的形式返回,如果不是,则返回throw new &lt;ErrorOfYourChoice&gt;() 和繁荣。设置好了。代码看起来(模糊地)像这样:

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

您可以检查this repository 以了解所有连接和工作的情况。使用POST /login -d 'username=test1&amp;password=changeme' 登录,获取 JWT 并随心所欲地使用它。

【讨论】:

嗨 Jay 解决方案不起作用。您还有其他建议吗? 我的建议是不要使用这种方法。它草率且难以管理。相反,只需正常使用守卫,而不是作为中间件 我自己喜欢这个问题,正在寻找一种方法来避免在每个解析器上使用防护。 然后告诉我什么不起作用,而不是“它不起作用”,因为当我为它创建 repo 时它起作用了 决定采纳您的建议并使用最佳方法。谢谢

以上是关于Nest.JS 使用 AuthGuard 作为 GraphQL 的中间件的主要内容,如果未能解决你的问题,请参考以下文章

Nest.js 中使用 @nestjs/passport 的可选身份验证

Nest.js Auth Guard JWT 身份验证不断返回 401 未授权

Nest JS / MongoDB - 地理空间索引在部署的服务器上不起作用

如何将firebase时间戳与graphql nest js一起使用?

Nest JS CORS 错误 0 未知,适用于 Android 浏览器,但适用于 PC 浏览器

ES2017 NEST JS @IsEmpty 在作为表达式调用时无法解析属性装饰器的签名。此表达式不可调用。不为空