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
This commit is contained in:
Brian Clozel
2026-05-11 11:26:32 +02:00
parent 18f306ac7f
commit 9a3cd2b4e7
5 changed files with 37 additions and 9 deletions
@@ -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
@@ -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<Object> getJsonConverter(HttpMessageConverters converters) {
@@ -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");
});
}
@@ -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");
});
}
@@ -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 = [