Spring GraphQL如何跳过introspection查询的鉴权逻辑
注意:内省查询会暴露全量Schema结构,存在安全风险,跳过鉴权放开内省的逻辑请仅在开发/测试环境使用,生产环境建议直接关闭内省能力。
方案1:快速适配(适合不想改动现有鉴权结构的开发场景)
直接利用GraphiQL可自定义请求头的能力做白名单判断,不需要解析GraphQL请求,实现成本最低:
- 在配置文件中给GraphiQL界面发起的所有请求加上固定标识头:
spring: graphql: graphiql: enabled: true headers: X-GraphiQL-Request: "true"
- 在你的
AuthFilter中优先判断请求头和路径,匹配则直接放行:
public class AuthFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().value(); // 放行GraphiQL静态资源、带GraphiQL标识头的请求 if (path.startsWith("/graphiql") || "true".equals(request.getHeaders().getFirst("X-GraphiQL-Request"))) { return chain.filter(exchange); } // 原有鉴权逻辑 // ... return chain.filter(exchange); } }
该方案基于请求头做信任判断,生产环境必须移除相关配置,避免攻击者伪造请求头绕过鉴权。
方案2:规范实现(推荐,无安全隐患)
WebFilter属于通用Web层组件,本身不感知GraphQL语义,不要在这一层解析GraphQL查询内容,应该把GraphQL相关的鉴权逻辑下沉到Spring GraphQL自带的扩展点,和Web层逻辑解耦:
- 调整
AuthFilter,直接放行GraphQL相关端点,这部分请求的鉴权交给GraphQL专用拦截器处理:
public class AuthFilter implements WebFilter { @Override public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { String path = exchange.getRequest().getPath().value(); // 放行GraphiQL静态资源、GraphQL端点,不做Web层鉴权 if (path.startsWith("/graphiql") || path.equals("/graphql")) { return chain.filter(exchange); } // 其他非GraphQL请求走原有鉴权逻辑 // ... return chain.filter(exchange); } }
- 自定义
WebGraphQlInterceptor处理GraphQL请求鉴权,在这里可以通过AST语法树精准识别纯内省查询,只对这类查询跳过鉴权,不存在误判:
@Component public class GraphQlAuthInterceptor implements WebGraphQlInterceptor { // 仅在GraphiQL开启时(开发环境)生效内省放行逻辑 @Value("${spring.graphql.graphiql.enabled:false}") private boolean allowIntrospectionNoAuth; @Override public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, WebGraphQlChain chain) { if (allowIntrospectionNoAuth && isPureIntrospectionQuery(request.getDocument())) { return chain.next(request); } // 其余GraphQL请求走原有鉴权逻辑,校验完成后可将用户信息写入GraphQL上下文 // ... 鉴权逻辑 return chain.next(request); } /** * 判断当前查询是否为纯内省查询(仅查询__schema/__type字段,无业务字段) */ private boolean isPureIntrospectionQuery(Document document) { for (Definition definition : document.getDefinitions()) { if (!(definition instanceof OperationDefinition opDef)) { continue; } for (Selection selection : opDef.getSelectionSet().getSelections()) { if (!(selection instanceof Field field)) { return false; } String name = field.getName(); // 包含任何非内省根字段,就需要走鉴权 if (!"__schema".equals(name) && !"__type".equals(name)) { return false; } } } return true; } }
避坑提醒
不要直接在WebFilter中读取/graphql端点的请求体来判断是不是内省查询:WebFlux的请求体是单次消费的流,直接读取后如果没有做缓存包装,后续Spring GraphQL层会因为读不到请求体直接报错。如果一定要在WebFilter层做解析,必须先通过ServerWebExchangeUtils.cacheRequestBodyAndRequest缓存请求体,维护成本很高,不推荐。
生产环境如果不需要对外提供Schema文档能力,直接关闭内省即可,不需要配置放行逻辑:
spring: graphql: schema: introspection: enabled: false
内容的提问来源于stack exchange,提问作者opuser1
相关产品推荐
相关产品推荐

