Spring Boot Security下如何开放GraphQL introspection访问?
解决GraphQL自省请求被Spring Security拦截的问题
问题分析
你的核心问题有两个:
- 自定义过滤器中直接调用
request.getReader()会消耗请求体流,导致后续Spring Security过滤器无法读取请求体完成JWT验证,这是过滤器失效的关键原因。 - 过滤器逻辑完全搞反:当前逻辑是仅允许自省请求通过,其他请求直接返回401,但你实际需要的是放行自省请求,其他请求继续走正常的JWT认证流程。
方案一:修复自定义过滤器
关键修改点
- 使用
ContentCachingRequestWrapper缓存请求体,避免流被一次性消耗,保证后续过滤器能正常读取。 - 调整逻辑:放行自省请求和Graphiql相关资源,其他请求继续执行完整的Security过滤器链。
修复后的过滤器代码
import org.springframework.web.util.ContentCachingRequestWrapper; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class IntrospectionQueryFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; // 包装请求体,缓存以便重复读取 ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(httpRequest); if (isIntrospectionQuery(wrappedRequest) || isGraphiqlResource(wrappedRequest)) { // 放行自省请求和Graphiql静态资源 chain.doFilter(wrappedRequest, response); } else { // 非自省请求,继续走正常的JWT认证流程 chain.doFilter(wrappedRequest, response); } } private boolean isGraphiqlResource(HttpServletRequest request) { String uri = request.getRequestURI(); return uri.equals("/graphiql") || uri.startsWith("/graphiql/") || uri.equals("/favicon.ico"); } private boolean isIntrospectionQuery(ContentCachingRequestWrapper request) throws IOException { if (!"POST".equalsIgnoreCase(request.getMethod()) || !"/graphql".equalsIgnoreCase(request.getRequestURI())) { return false; } // 从缓存中读取请求体,避免流被消耗 String body = new String(request.getContentAsByteArray(), request.getCharacterEncoding()); return body.contains("\"operationName\":\"IntrospectionQuery\"") || body.contains("__schema") // 兼容不带operationName的自省请求 || body.contains("__type"); } @Override public void destroy() {} }
过滤器配置调整
确保过滤器添加在JWT认证过滤器之前(而非usernamePasswordAuthenticationFilter,因为你用的是JWT体系,可能不存在这个过滤器):
@Configuration @EnableWebSecurity public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthenticationFilter; public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { this.jwtAuthenticationFilter = jwtAuthenticationFilter; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/graphiql/**", "/favicon.ico").permitAll() .anyRequest().authenticated() ) // 将自省过滤器放在JWT认证过滤器之前 .addFilterBefore(new IntrospectionQueryFilter(), JwtAuthenticationFilter.class); return http.build(); } }
方案二:无需自定义过滤器,直接在Security配置中处理
利用Spring GraphQL提供的工具类判断自省请求,实现更简洁:
import org.springframework.graphql.server.WebGraphQlRequest; import org.springframework.graphql.server.WebGraphQlRequestBuilder; import org.springframework.security.web.util.matcher.RequestMatcher; import javax.servlet.http.HttpServletRequest; import java.io.IOException; @Configuration @EnableWebSecurity public class SecurityConfig { private final JwtAuthenticationFilter jwtAuthenticationFilter; public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) { this.jwtAuthenticationFilter = jwtAuthenticationFilter; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf -> csrf.disable()) .authorizeHttpRequests(auth -> auth .requestMatchers("/graphiql/**", "/favicon.ico").permitAll() // 放行自省请求 .requestMatchers(introspectionRequestMatcher()).permitAll() .anyRequest().authenticated() ) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } private RequestMatcher introspectionRequestMatcher() { return request -> { if (!"POST".equalsIgnoreCase(request.getMethod()) || !"/graphql".equals(request.getRequestURI())) { return false; } try { WebGraphQlRequest graphQlRequest = new WebGraphQlRequestBuilder() .request((HttpServletRequest) request) .build(); return graphQlRequest.isIntrospectionQuery(); } catch (IOException e) { return false; } }; } }
额外注意事项
- 方案二需要Spring Boot 3.x + Spring GraphQL 1.2及以上版本,才支持
WebGraphQlRequest.isIntrospectionQuery()方法。 - 若使用Spring Boot 2.x,可复用方案一中的请求体解析逻辑来判断自省请求。
内容的提问来源于stack exchange,提问作者Gregg
相关产品推荐
相关产品推荐

