From 07611ef8f895be75d1dc13e4cc13165f95db8546 Mon Sep 17 00:00:00 2001 From: Tony Clarke Date: Thu, 28 Jun 2018 13:41:59 -0400 Subject: [PATCH] Support global configuration for CORS. See https://github.com/spring-cloud/spring-cloud-gateway/issues/229 --- .../config/GatewayAutoConfiguration.java | 11 ++- .../gateway/config/GlobalCorsProperties.java | 40 ++++++++ .../handler/RoutePredicateHandlerMapping.java | 12 ++- .../cloud/gateway/cors/CorsTests.java | 93 +++++++++++++++++++ .../src/test/resources/application.yml | 5 + 5 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java create mode 100644 spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java index b936e620..23ff89a8 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GatewayAutoConfiguration.java @@ -280,9 +280,14 @@ public class GatewayAutoConfiguration { } @Bean - public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, - RouteLocator routeLocator) { - return new RoutePredicateHandlerMapping(webHandler, routeLocator); + public GlobalCorsProperties globalCorsProperties() { + return new GlobalCorsProperties(); + } + + @Bean + public RoutePredicateHandlerMapping routePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, + GlobalCorsProperties globalCorsProperties) { + return new RoutePredicateHandlerMapping(webHandler, routeLocator, globalCorsProperties); } // ConfigurationProperty beans diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java new file mode 100644 index 00000000..8216d2f3 --- /dev/null +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/config/GlobalCorsProperties.java @@ -0,0 +1,40 @@ +/* + * Copyright 2013-2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.cloud.gateway.config; + +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping; +import org.springframework.web.cors.CorsConfiguration; + +/** + * Configuration properties for global configuration of cors. See {@link RoutePredicateHandlerMapping} + */ +@ConfigurationProperties("spring.cloud.gateway.globalcors") +public class GlobalCorsProperties { + + private final Map corsConfigurations = new LinkedHashMap<>(); + + public Map getCorsConfigurations() + { + return corsConfigurations; + } + +} diff --git a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java index 53fdf340..665e9294 100644 --- a/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java +++ b/spring-cloud-gateway-core/src/main/java/org/springframework/cloud/gateway/handler/RoutePredicateHandlerMapping.java @@ -19,6 +19,7 @@ package org.springframework.cloud.gateway.handler; import java.util.function.Function; +import org.springframework.cloud.gateway.config.GlobalCorsProperties; import org.springframework.cloud.gateway.route.Route; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.web.cors.CorsConfiguration; @@ -39,11 +40,12 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { private final FilteringWebHandler webHandler; private final RouteLocator routeLocator; - public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator) { + public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties) { this.webHandler = webHandler; this.routeLocator = routeLocator; - setOrder(1); + setOrder(1); + setCorsConfigurations(globalCorsProperties.getCorsConfigurations()); } @Override @@ -70,10 +72,10 @@ public class RoutePredicateHandlerMapping extends AbstractHandlerMapping { @Override protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) { - //TODO: support cors configuration via global properties and - // properties on a route see gh-229 + // TODO: support cors configuration via properties on a route see gh-229 // see RequestMappingHandlerMapping.initCorsConfiguration() - // also see https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java + // also see https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/test/java/org/springframework/web/cors/reactive/CorsWebFilterTests.java + return super.getCorsConfiguration(handler, exchange); } diff --git a/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java new file mode 100644 index 00000000..7aef6073 --- /dev/null +++ b/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/cors/CorsTests.java @@ -0,0 +1,93 @@ +/* + * Copyright 2013-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.cloud.gateway.cors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.test.BaseWebClientTests; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.ClientResponse; + +import reactor.core.publisher.Mono; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +@DirtiesContext +public class CorsTests extends BaseWebClientTests { + + @Test + public void testPreFlightCorsRequest() { + ClientResponse clientResponse = webClient.options().uri("/abc/123/function") + .header("Origin", "domain.com") + .header("Access-Control-Request-Method", "GET") + .exchange().block(); + HttpHeaders asHttpHeaders = clientResponse.headers().asHttpHeaders(); + Mono bodyToMono = clientResponse.bodyToMono(String.class); + //pre-flight request shouldn't return the response body + assertNull(bodyToMono.block()); + assertEquals("Missing header value in response: "+HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"*", asHttpHeaders.getAccessControlAllowOrigin()); + assertEquals("Pre Flight call failed.", HttpStatus.OK, clientResponse.statusCode()); + } + + @Test + public void testCorsRequest() { + ClientResponse clientResponse = webClient.get().uri("/abc/123/function") + .header("Origin", "domain.com") + .header(HttpHeaders.HOST, "www.path.org") + .exchange().block(); + HttpHeaders asHttpHeaders = clientResponse.headers().asHttpHeaders(); + Mono bodyToMono = clientResponse.bodyToMono(String.class); + assertNotNull(bodyToMono.block()); + assertEquals("Missing header value in response: "+HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,"*", asHttpHeaders.getAccessControlAllowOrigin()); + assertEquals("Pre Flight call failed.", HttpStatus.OK, clientResponse.statusCode()); + } + + @EnableAutoConfiguration + @SpringBootConfiguration + @Import(DefaultTestConfig.class) + public static class TestConfig { + + // this enables the access-control-allow-origin header from the target MS +// @Bean +// public WebFluxConfigurer corsConfigurer() { +// return new WebFluxConfigurerComposite() { +// +// @Override +// public void addCorsMappings(CorsRegistry registry) { +// registry.addMapping("/**") +// .allowedOrigins("*") +// .allowedMethods("*"); +// } +// }; +// } + + } + +} diff --git a/spring-cloud-gateway-core/src/test/resources/application.yml b/spring-cloud-gateway-core/src/test/resources/application.yml index dc2bb2eb..5dbdfa9c 100644 --- a/spring-cloud-gateway-core/src/test/resources/application.yml +++ b/spring-cloud-gateway-core/src/test/resources/application.yml @@ -7,6 +7,11 @@ test: spring: cloud: gateway: + globalcors: + corsConfigurations: + '[/**]': + maxAge: 10 + allowedOrigins: "*" default-filters: - AddResponseHeader=X-Response-Default-Foo, Default-Bar - PrefixPath=/httpbin