如何在NestJS中为公共访问禁用GraphQL自省功能
在NestJS中禁用GraphQL自省及保护私有API的方案
一、禁用GraphQL自省功能
针对Apollo驱动
在GraphQLModule的配置中,通过introspection字段控制自省开关,推荐根据环境变量动态设置(生产环境禁用):
import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; @Module({ imports: [ GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: true, // 生产环境关闭自省,开发环境保留 introspection: process.env.NODE_ENV !== 'production', // 若要完全禁止公共访问,直接设为false即可 // introspection: false, }), ], }) export class AppModule {}
针对Mercurius驱动
配置逻辑与Apollo一致,修改对应的驱动类型即可:
import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { MercuriusDriver, MercuriusDriverConfig } from '@nestjs/mercurius'; @Module({ imports: [ GraphQLModule.forRoot<MercuriusDriverConfig>({ driver: MercuriusDriver, autoSchemaFile: true, introspection: process.env.NODE_ENV !== 'production', }), ], }) export class AppModule {}
细粒度控制:仅允许授权用户访问自省
如果需要保留自省但仅对授权用户开放,可通过拦截器实现:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, UnauthorizedException } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class IntrospectionAuthInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const gqlContext = context.getArgByIndex(2); const query = gqlContext.req.body.query; // 判断是否为自省查询(匹配__schema或__type关键字) if (query?.includes('__schema') || query?.includes('__type')) { // 复用你已有的用户授权逻辑 const user = gqlContext.req.user; if (!user) { throw new UnauthorizedException('无权访问Schema信息'); } } return next.handle(); } }
在AppModule中全局注册该拦截器:
import { APP_INTERCEPTOR } from '@nestjs/core'; @Module({ providers: [ { provide: APP_INTERCEPTOR, useClass: IntrospectionAuthInterceptor, }, ], }) export class AppModule {}
二、保护特定私有API
方法1:用AuthGuard直接保护Resolver
给私有查询/突变添加@UseGuards(AuthGuard)装饰器,复用你已有的授权守卫:
import { Resolver, Query, UseGuards } from '@nestjs/graphql'; import { AuthGuard } from './auth.guard'; @Resolver() export class PrivateResolver { @Query(() => String) @UseGuards(AuthGuard) getSensitiveData() { return '仅限授权用户访问的敏感数据'; } }
方法2:自定义装饰器+拦截器实现灵活控制
如果需要批量标记或更复杂的权限逻辑,可通过自定义装饰器和拦截器组合实现:
- 定义私有API装饰器:
import { SetMetadata } from '@nestjs/common'; export const IS_PRIVATE = 'isPrivate'; export const Private = () => SetMetadata(IS_PRIVATE, true);
- 在Resolver中标记私有接口:
import { Resolver, Query } from '@nestjs/graphql'; import { Private } from './private.decorator'; @Resolver() export class UserResolver { @Query(() => User) @Private() getUserPrivateProfile(@Args('id') id: string) { // 返回用户私有信息 } }
- 实现拦截器验证权限:
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, UnauthorizedException } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { Observable } from 'rxjs'; import { IS_PRIVATE } from './private.decorator'; @Injectable() export class PrivateApiInterceptor implements NestInterceptor { constructor(private reflector: Reflector) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const isPrivate = this.reflector.getAllAndOverride<boolean>(IS_PRIVATE, [ context.getHandler(), context.getClass(), ]); const gqlContext = context.getArgByIndex(2); const user = gqlContext.req.user; if (isPrivate && !user) { throw new UnauthorizedException('无权访问该私有API'); } return next.handle(); } }
最后在AppModule中全局注册拦截器即可。
内容的提问来源于stack exchange,提问作者Ahmad Salman Khan
相关产品推荐
相关产品推荐

