You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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:自定义装饰器+拦截器实现灵活控制

如果需要批量标记或更复杂的权限逻辑,可通过自定义装饰器和拦截器组合实现:

  1. 定义私有API装饰器:
import { SetMetadata } from '@nestjs/common';

export const IS_PRIVATE = 'isPrivate';
export const Private = () => SetMetadata(IS_PRIVATE, true);
  1. 在Resolver中标记私有接口:
import { Resolver, Query } from '@nestjs/graphql';
import { Private } from './private.decorator';

@Resolver()
export class UserResolver {
  @Query(() => User)
  @Private()
  getUserPrivateProfile(@Args('id') id: string) {
    // 返回用户私有信息
  }
}
  1. 实现拦截器验证权限:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 04:15:40