我正在尝试使用 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。
但是,我想保护整个端点,这样就不会为每个受保护的解析器调用守卫,而只在主请求上调用一次。这可能吗?
这在技术上是可行的,但写起来相当草率,而且绝对不能保证它能与 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 并随心所欲地使用它。
参见: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;
}
}
但是,我想保护整个端点,这样就不会为每个受保护的解析器调用守卫,而只在主请求上调用一次。这可能吗?
通过使用 NestJS 的参考全局方法,我能够获得一个中间件函数来解析每个查询/突变:https://docs.nestjs.com/graphql/field-middleware#global-field-middleware。