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

如何在SpringBoot中用Netflix Dgs暴露公私两个GraphQL端点?

SpringBoot + Netflix Dgs 实现公开/私有API两种方案指引

方案一:双API端点(公开/私有分离)

核心思路

通过注册两个独立的Dgs GraphQL Servlet,分别映射到不同路径,给私有端点配置认证拦截,同时限制公开端点仅加载允许的查询/变更操作。

实现步骤

  1. 注册双Servlet端点
    自定义配置类,创建两个DgsGraphQLServlet实例,分别对应公开和私有API,映射到不同路径(如/public/graphql和/private/graphql)。同时为公开端点筛选仅允许的DataFetcher组件:

    @Configuration
    public class DgsMultiEndpointConfig {
        // 公开API Servlet:仅加载标记为@PublicApi的组件
        @Bean
        public ServletRegistrationBean<DgsGraphQLServlet> publicDgsServlet(
            DgsGraphQLPublicContext publicContext) {
            DgsGraphQLServlet servlet = new DgsGraphQLServlet(publicContext);
            return new ServletRegistrationBean<>(servlet, "/public/graphql");
        }
    
        // 私有API Servlet:加载所有DataFetcher组件
        @Bean
        public ServletRegistrationBean<DgsGraphQLServlet> privateDgsServlet(
            DgsGraphQLContext privateContext) {
            DgsGraphQLServlet servlet = new DgsGraphQLServlet(privateContext);
            return new ServletRegistrationBean<>(servlet, "/private/graphql");
        }
    
        // 公开Context:筛选公开组件
        @Bean
        public DgsGraphQLPublicContext publicDgsContext(List<DgsComponent> allComponents) {
            List<DgsComponent> publicComponents = allComponents.stream()
                .filter(c -> c.getClass().isAnnotationPresent(PublicApi.class))
                .collect(Collectors.toList());
            return new DgsGraphQLPublicContext(publicComponents);
        }
    
        // 私有Context:使用所有组件
        @Bean
        public DgsGraphQLContext privateDgsContext(List<DgsComponent> allComponents) {
            return new DgsGraphQLContext(allComponents);
        }
    }
    
    // 自定义标记注解
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PublicApi {}
    
  2. 配置Spring Security拦截规则
    对私有端点强制认证,公开端点直接放行:

    @Configuration
    @EnableWebSecurity
    public class SecurityConfig {
        @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
            http
                .authorizeHttpRequests(auth -> auth
                    .requestMatchers("/public/graphql").permitAll()
                    .requestMatchers("/private/graphql").authenticated()
                    .anyRequest().denyAll()
                )
                // 根据你的认证方式配置,比如JWT、OAuth2等
                .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
            return http.build();
        }
    }
    
  3. 标记公开DataFetcher
    在允许公开的DataFetcher类上添加@PublicApi注解,确保只有这些组件被加载到公开端点。


方案二:单端点下的部分操作公开

核心思路

通过方法级安全或自定义拦截器,在单端点内区分公开/私有操作,无需拆分端点。

实现方式1:方法级安全注解

利用Spring Security的@PreAuthorize注解,直接在DataFetcher方法上标记权限要求:

  1. 开启方法级安全:
    @Configuration
    @EnableMethodSecurity
    public class MethodSecurityConfig {}
    
  2. 在DataFetcher中标记权限:
    @DgsComponent
    public class PublicQueries {
        // 公开查询:允许所有访问
        @DgsQuery(field = "publicHello")
        @PreAuthorize("permitAll()")
        public String publicHello() {
            return "Hello from public API";
        }
    }
    
    @DgsComponent
    public class PrivateQueries {
        // 私有查询:仅认证用户可访问
        @DgsQuery(field = "currentUser")
        @PreAuthorize("isAuthenticated()")
        public User currentUser() {
            // 返回当前认证用户数据
            return null;
        }
    }
    

实现方式2:自定义Schema Directive拦截

通过GraphQL自定义指令标记公开操作,再用Dgs拦截器校验权限:

  1. 在GraphQL Schema中添加@public指令:
    directive @public on QUERY | MUTATION
    
    type Query {
        publicHello: String! @public
        currentUser: User!
    }
    
    type Mutation {
        publicSignup(input: SignupInput!): User! @public
        updateProfile(input: ProfileInput!): User!
    }
    
  2. 实现Dgs数据拦截器:
    @Component
    public class PublicApiInterceptor implements DgsDataFetchingInterceptor {
        @Override
        public CompletableFuture<Object> intercept(
            DgsDataFetchingEnvironment env, DataFetcher<Object> dataFetcher) {
            // 检查当前操作是否标记为@public
            boolean isPublic = env.getFieldDefinition().getDirectives().stream()
                .anyMatch(d -> d.getName().equals("public"));
            
            if (isPublic) {
                return dataFetcher.get(env);
            }
    
            // 非公开操作校验认证状态
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth == null || !auth.isAuthenticated()) {
                throw new UnauthorizedException("Authentication required for this operation");
            }
    
            return dataFetcher.get(env);
        }
    }
    

内容的提问来源于stack exchange,提问作者justrying

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 09:24:56