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

如何用AWS Cognito实现Spring GraphQL订阅的JWT认证?

问题描述

我用Spring for GraphQL写了一个Java GraphQL服务器,打算通过WebSocket的GraphQL订阅给React客户端推送数据,需要部署到AWS EKS,并用AWS Cognito的JWT做流量认证。想请教怎么实现?能不能配置Spring Security验证Cognito的JWT来支持GraphQL订阅?

我尝试用org.springframework.graphql.server.WebSocketGraphQlInterceptor但没成功,加了Spring Security后所有流量都被拦截,调试时根本进不了这个拦截器。

编辑补充:我已经配置了DefaultSecurityFilterChain和WebSocketGraphQlInterceptor,但拦截器只能捕获mutation,处理不了订阅——触发订阅时一直返回401 Unauthorized,根本不会进入handleConnectionInitialization方法。

我的SecurityFilterChain代码如下:

@Bean
public DefaultSecurityFilterChain graphqlSecurityChain(HttpSecurity http) throws Exception {

    return http.cors(AbstractHttpConfigurer::disable)
        .csrf(AbstractHttpConfigurer::disable)
        .sessionManagement(
            session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(
            auth ->
                auth.requestMatchers(HttpMethod.GET, "/subscription")
                    .permitAll()
                    .anyRequest()
                    .authenticated())
        .oauth2ResourceServer(
            oauthConfigurer ->
                oauthConfigurer.jwt(
                    jwtConfigurer -> jwtConfigurer.decoder(JwtDecoders.fromIssuerLocation(issuer))))
        .build();
}

请问WebSocketGraphQlInterceptor无法正常处理订阅的原因是什么?


问题分析与解决方案

核心原因

你的问题根源在于Spring Security的拦截优先级高于GraphQL WebSocket拦截器,且对WebSocket握手请求的认证逻辑处理错误:

  1. GraphQL订阅的WebSocket握手默认是GET /graphql请求,但你配置的/subscription路径和实际端点不匹配,导致握手请求被anyRequest().authenticated()拦截。
  2. 即使路径匹配,你把/subscription设为permitAll(),会跳过Spring Security的JWT验证,同时Spring Security会先于GraphQL拦截器处理握手请求,没有有效JWT时直接返回401,根本到不了WebSocketGraphQlInterceptor的初始化方法。

正确配置步骤

1. 修正Spring Security配置

确保WebSocket握手请求能通过JWT验证,而非直接放行:

@Bean
public DefaultSecurityFilterChain graphqlSecurityChain(HttpSecurity http) throws Exception {
    return http.cors(AbstractHttpConfigurer::disable)
        .csrf(AbstractHttpConfigurer::disable)
        .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(auth ->
            // 所有GraphQL请求(含WebSocket握手)需认证
            auth.requestMatchers("/graphql").authenticated()
                .anyRequest().permitAll()) // 非GraphQL请求按需配置
        .oauth2ResourceServer(oauthConfigurer ->
            oauthConfigurer.jwt(jwtConfigurer ->
                jwtConfigurer.decoder(JwtDecoders.fromIssuerLocation(issuer))))
        .build();
}
  • 若你的GraphQL WebSocket端点不是默认的/graphql,替换为实际路径。
  • 不要将握手路径设为permitAll(),否则Spring Security不会验证JWT,后续GraphQL上下文也无法获取认证信息。

2. 配置WebSocketGraphQlInterceptor传递认证上下文

Spring Security完成JWT验证后,会将认证信息存入SecurityContextHolder,此时拦截器可在连接初始化时将信息传递到订阅上下文:

@Component
public class AuthWebSocketInterceptor implements WebSocketGraphQlInterceptor {

    @Override
    public Mono<Object> handleConnectionInitialization(WebSocketSession session, Map<String, Object> connectionInitPayload) {
        // 从SecurityContext获取已认证用户信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.isAuthenticated()) {
            // 将用户信息存入WebSocket会话属性
            session.getAttributes().put("user", authentication.getPrincipal());
        }
        return Mono.empty(); // 继续连接初始化流程
    }

    @Override
    public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
        // 将会话中的用户信息放入GraphQL请求上下文
        Authentication authentication = (Authentication) request.getSessionAttributes().get("user");
        if (authentication != null) {
            request.configureExecutionInput((input, builder) ->
                builder.graphQLContext(context -> context.put("user", authentication)).build());
        }
        return chain.next(request);
    }
}

3. 确认GraphQL WebSocket端点配置

在application.yml中验证端点路径与Security配置一致:

spring:
  graphql:
    websocket:
      path: /graphql

4. EKS部署注意事项

  • 确保Ingress控制器支持WebSocket:比如AWS ALB需开启WebSocket代理;NGINX Ingress需添加配置:
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    
  • 将Cognito的Issuer URL配置为环境变量,避免硬编码,确保应用能正确初始化JWT解码器。

为什么之前的拦截器不生效?

你的Spring Security配置把/subscription设为permitAll(),但实际WebSocket握手请求是到默认的/graphql路径,导致该请求被anyRequest().authenticated()拦截。若请求头未携带有效JWT,Spring Security直接返回401,完全没机会触发WebSocketGraphQlInterceptor的handleConnectionInitialization方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 06:20:35