From 9a3cd2b4e7d327b1c1190099f5edc20e537d21db Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Mon, 11 May 2026 11:26:32 +0200 Subject: [PATCH] Configure allowed origins for GraphQL WebSocket support As of spring-projects/spring-graphql#1465, Spring for GraphQL also supports applying allowed origins to the websocket endpoint. As it's considered a bug of omission in that project, this commit adapts the Spring Boot auto-configuration to leverage this change. There is no need for new configuration properties, as `spring.graphql.cors.*` origin-related keys apply to all supported transports. Fixes gh-50391 --- .../reactive/GraphQlWebFluxAutoConfiguration.java | 14 +++++++++++--- .../servlet/GraphQlWebMvcAutoConfiguration.java | 14 +++++++++++--- .../GraphQlWebFluxAutoConfigurationTests.java | 8 +++++++- .../GraphQlWebMvcAutoConfigurationTests.java | 8 +++++++- .../spring-boot-dependencies/build.gradle | 2 +- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java index a2b2bb9cdc7..73849b37860 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfiguration.java @@ -175,9 +175,17 @@ public class GraphQlWebFluxAutoConfiguration { @Bean @ConditionalOnMissingBean public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler, - GraphQlProperties properties, ServerCodecConfigurer configurer) { - return new GraphQlWebSocketHandler(webGraphQlHandler, configurer, - properties.getWebsocket().getConnectionInitTimeout(), properties.getWebsocket().getKeepAlive()); + GraphQlProperties properties, GraphQlCorsProperties corsProps, ServerCodecConfigurer configurer) { + CorsConfiguration corsConfiguration = corsProps.toCorsConfiguration(); + if (corsConfiguration != null) { + return new GraphQlWebSocketHandler(webGraphQlHandler, configurer, + properties.getWebsocket().getConnectionInitTimeout(), properties.getWebsocket().getKeepAlive(), + corsConfiguration); + } + else { + return new GraphQlWebSocketHandler(webGraphQlHandler, configurer, + properties.getWebsocket().getConnectionInitTimeout(), properties.getWebsocket().getKeepAlive()); + } } @Bean diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java index 1fc55704aa2..8185718892d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfiguration.java @@ -180,9 +180,17 @@ public class GraphQlWebMvcAutoConfiguration { @Bean @ConditionalOnMissingBean public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler, - GraphQlProperties properties, HttpMessageConverters converters) { - return new GraphQlWebSocketHandler(webGraphQlHandler, getJsonConverter(converters), - properties.getWebsocket().getConnectionInitTimeout(), properties.getWebsocket().getKeepAlive()); + GraphQlProperties properties, GraphQlCorsProperties corsProperties, HttpMessageConverters converters) { + CorsConfiguration corsConfiguration = corsProperties.toCorsConfiguration(); + if (corsConfiguration != null) { + return new GraphQlWebSocketHandler(webGraphQlHandler, getJsonConverter(converters), + properties.getWebsocket().getConnectionInitTimeout(), properties.getWebsocket().getKeepAlive(), + corsConfiguration); + } + else { + return new GraphQlWebSocketHandler(webGraphQlHandler, getJsonConverter(converters), + properties.getWebsocket().getConnectionInitTimeout(), properties.getWebsocket().getKeepAlive()); + } } private GenericHttpMessageConverter getJsonConverter(HttpMessageConverters converters) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java index 51f5c4b2cca..889b4d85a9d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/reactive/GraphQlWebFluxAutoConfigurationTests.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.function.Consumer; import graphql.schema.idl.TypeRuntimeWiring; +import org.assertj.core.api.InstanceOfAssertFactories; import org.junit.jupiter.api.Test; import org.springframework.aot.hint.RuntimeHints; @@ -262,13 +263,18 @@ class GraphQlWebFluxAutoConfigurationTests { void shouldConfigureWebSocketProperties() { this.contextRunner .withPropertyValues("spring.graphql.websocket.path=/ws", - "spring.graphql.websocket.connection-init-timeout=120s", "spring.graphql.websocket.keep-alive=30s") + "spring.graphql.websocket.connection-init-timeout=120s", "spring.graphql.websocket.keep-alive=30s", + "spring.graphql.cors.allowed-origins=https://example.com") .run((context) -> { assertThat(context).hasSingleBean(GraphQlWebSocketHandler.class); GraphQlWebSocketHandler graphQlWebSocketHandler = context.getBean(GraphQlWebSocketHandler.class); assertThat(graphQlWebSocketHandler).extracting("initTimeoutDuration") .isEqualTo(Duration.ofSeconds(120)); assertThat(graphQlWebSocketHandler).extracting("keepAliveDuration").isEqualTo(Duration.ofSeconds(30)); + assertThat(graphQlWebSocketHandler).extracting("corsConfiguration") + .extracting("allowedOrigins") + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactly("https://example.com"); }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java index 533b0a28ed2..f4aee237a19 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/graphql/servlet/GraphQlWebMvcAutoConfigurationTests.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import graphql.schema.idl.TypeRuntimeWiring; +import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.ThrowingConsumer; import org.junit.jupiter.api.Test; @@ -243,13 +244,18 @@ class GraphQlWebMvcAutoConfigurationTests { void shouldConfigureWebSocketProperties() { this.contextRunner .withPropertyValues("spring.graphql.websocket.path=/ws", - "spring.graphql.websocket.connection-init-timeout=120s", "spring.graphql.websocket.keep-alive=30s") + "spring.graphql.websocket.connection-init-timeout=120s", "spring.graphql.websocket.keep-alive=30s", + "spring.graphql.cors.allowed-origins=https://example.com") .run((context) -> { assertThat(context).hasSingleBean(GraphQlWebSocketHandler.class); GraphQlWebSocketHandler graphQlWebSocketHandler = context.getBean(GraphQlWebSocketHandler.class); assertThat(graphQlWebSocketHandler).extracting("initTimeoutDuration") .isEqualTo(Duration.ofSeconds(120)); assertThat(graphQlWebSocketHandler).extracting("keepAliveDuration").isEqualTo(Duration.ofSeconds(30)); + assertThat(graphQlWebSocketHandler).extracting("corsConfiguration") + .extracting("allowedOrigins") + .asInstanceOf(InstanceOfAssertFactories.LIST) + .containsExactly("https://example.com"); }); } diff --git a/spring-boot-project/spring-boot-dependencies/build.gradle b/spring-boot-project/spring-boot-dependencies/build.gradle index bdb4e0ba460..7d3d2f7ea00 100644 --- a/spring-boot-project/spring-boot-dependencies/build.gradle +++ b/spring-boot-project/spring-boot-dependencies/build.gradle @@ -2317,7 +2317,7 @@ bom { releaseNotes("https://github.com/spring-projects/spring-framework/releases/tag/v{version}") } } - library("Spring GraphQL", "1.4.5") { + library("Spring GraphQL", "1.4.6-SNAPSHOT") { considerSnapshots() group("org.springframework.graphql") { modules = [