diff --git a/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java b/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java index 7fb1b88c4d1..a7c7e7474c9 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java +++ b/spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -112,6 +112,8 @@ public @interface CrossOrigin { * {@code Expires}, {@code Last-Modified}, or {@code Pragma}, *
Exposed headers are listed in the {@code Access-Control-Expose-Headers} * response header of actual CORS requests. + *
The special value {@code "*"} allows all headers to be exposed for + * non-credentialed requests. *
By default no headers are listed as exposed. */ String[] exposedHeaders() default {}; diff --git a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java index 884a13add2a..afea5508c52 100644 --- a/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java +++ b/spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -254,13 +254,11 @@ public class CorsConfiguration { * {@code Cache-Control}, {@code Content-Language}, {@code Content-Type}, * {@code Expires}, {@code Last-Modified}, or {@code Pragma}) that an * actual response might have and can be exposed. - *
Note that {@code "*"} is not a valid exposed header value. + *
The special value {@code "*"} allows all headers to be exposed for + * non-credentialed requests. *
By default this is not set.
*/
public void setExposedHeaders(@Nullable List Note that {@code "*"} is not a valid exposed header value.
+ * The special value {@code "*"} allows all headers to be exposed for
+ * non-credentialed requests.
*/
public void addExposedHeader(String exposedHeader) {
- if (ALL.equals(exposedHeader)) {
- throw new IllegalArgumentException("'*' is not a valid exposed header value");
- }
if (this.exposedHeaders == null) {
this.exposedHeaders = new ArrayList<>(4);
}
diff --git a/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java b/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java
index c18a456111b..ca7158aa407 100644
--- a/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java
+++ b/spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -25,7 +25,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.http.HttpMethod;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Unit tests for {@link CorsConfiguration}.
@@ -61,29 +60,13 @@ public class CorsConfigurationTests {
assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
config.addAllowedMethod("*");
assertThat(config.getAllowedMethods()).isEqualTo(Arrays.asList("*"));
- config.addExposedHeader("header1");
- config.addExposedHeader("header2");
- assertThat(config.getExposedHeaders()).isEqualTo(Arrays.asList("header1", "header2"));
+ config.addExposedHeader("*");
config.setAllowCredentials(true);
assertThat((boolean) config.getAllowCredentials()).isTrue();
config.setMaxAge(123L);
assertThat(config.getMaxAge()).isEqualTo(new Long(123));
}
- @Test
- public void asteriskWildCardOnAddExposedHeader() {
- CorsConfiguration config = new CorsConfiguration();
- assertThatIllegalArgumentException().isThrownBy(() ->
- config.addExposedHeader("*"));
- }
-
- @Test
- public void asteriskWildCardOnSetExposedHeaders() {
- CorsConfiguration config = new CorsConfiguration();
- assertThatIllegalArgumentException().isThrownBy(() ->
- config.setExposedHeaders(Arrays.asList("*")));
- }
-
@Test
public void combineWithNull() {
CorsConfiguration config = new CorsConfiguration();
@@ -120,26 +103,34 @@ public class CorsConfigurationTests {
other.addAllowedMethod(HttpMethod.PUT.name());
CorsConfiguration combinedConfig = config.combine(other);
- assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("https://domain.com"));
- assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("header1"));
- assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.PUT.name()));
+ assertThat(combinedConfig).isNotNull();
+ assertThat(combinedConfig.getAllowedOrigins()).containsExactly("https://domain.com");
+ assertThat(combinedConfig.getAllowedHeaders()).containsExactly("header1");
+ assertThat(combinedConfig.getAllowedMethods()).containsExactly(HttpMethod.PUT.name());
+ assertThat(combinedConfig.getExposedHeaders()).isEmpty();
combinedConfig = other.combine(config);
- assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("https://domain.com"));
- assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("header1"));
- assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.PUT.name()));
+ assertThat(combinedConfig).isNotNull();
+ assertThat(combinedConfig.getAllowedOrigins()).containsExactly("https://domain.com");
+ assertThat(combinedConfig.getAllowedHeaders()).containsExactly("header1");
+ assertThat(combinedConfig.getAllowedMethods()).containsExactly(HttpMethod.PUT.name());
+ assertThat(combinedConfig.getExposedHeaders()).isEmpty();
combinedConfig = config.combine(new CorsConfiguration());
- assertThat(config.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
- assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
- assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
- HttpMethod.POST.name()));
+ assertThat(config.getAllowedOrigins()).containsExactly("*");
+ assertThat(config.getAllowedHeaders()).containsExactly("*");
+ assertThat(combinedConfig).isNotNull();
+ assertThat(combinedConfig.getAllowedMethods())
+ .containsExactly(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name());
+ assertThat(combinedConfig.getExposedHeaders()).isEmpty();
combinedConfig = new CorsConfiguration().combine(config);
- assertThat(config.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
- assertThat(config.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
- assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
- HttpMethod.POST.name()));
+ assertThat(config.getAllowedOrigins()).containsExactly("*");
+ assertThat(config.getAllowedHeaders()).containsExactly("*");
+ assertThat(combinedConfig).isNotNull();
+ assertThat(combinedConfig.getAllowedMethods())
+ .containsExactly(HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name());
+ assertThat(combinedConfig.getExposedHeaders()).isEmpty();
}
@Test
@@ -147,20 +138,29 @@ public class CorsConfigurationTests {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
+ config.addExposedHeader("*");
config.addAllowedMethod("*");
CorsConfiguration other = new CorsConfiguration();
other.addAllowedOrigin("https://domain.com");
other.addAllowedHeader("header1");
other.addExposedHeader("header2");
+ other.addAllowedHeader("anotherHeader1");
+ other.addExposedHeader("anotherHeader2");
other.addAllowedMethod(HttpMethod.PUT.name());
CorsConfiguration combinedConfig = config.combine(other);
- assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
- assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
- assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList("*"));
+ assertThat(combinedConfig).isNotNull();
+ assertThat(combinedConfig.getAllowedOrigins()).containsExactly("*");
+ assertThat(combinedConfig.getAllowedHeaders()).containsExactly("*");
+ assertThat(combinedConfig.getExposedHeaders()).containsExactly("*");
+ assertThat(combinedConfig.getAllowedMethods()).containsExactly("*");
+
combinedConfig = other.combine(config);
- assertThat(combinedConfig.getAllowedOrigins()).isEqualTo(Arrays.asList("*"));
- assertThat(combinedConfig.getAllowedHeaders()).isEqualTo(Arrays.asList("*"));
- assertThat(combinedConfig.getAllowedMethods()).isEqualTo(Arrays.asList("*"));
+ assertThat(combinedConfig).isNotNull();
+ assertThat(combinedConfig.getAllowedOrigins()).containsExactly("*");
+ assertThat(combinedConfig.getAllowedHeaders()).containsExactly("*");
+ assertThat(combinedConfig.getExposedHeaders()).containsExactly("*");
+ assertThat(combinedConfig.getAllowedMethods()).containsExactly("*");
+ assertThat(combinedConfig.getAllowedHeaders()).containsExactly("*");
}
@Test // SPR-14792
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java b/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java
index 096935e79da..06301f82f25 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -93,7 +93,8 @@ public class CorsRegistration {
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}, that an
* actual response might have and can be exposed.
- * Note that {@code "*"} is not supported on this property.
+ * The special value {@code "*"} allows all headers to be exposed for
+ * non-credentialed requests.
* By default this is not set.
*/
public CorsRegistration exposedHeaders(String... headers) {
diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java
index a7bb9371abc..b30748fbfd4 100644
--- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java
+++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2020 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.
@@ -95,7 +95,8 @@ public class CorsRegistration {
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}, that an
* actual response might have and can be exposed.
- * Note that {@code "*"} is not supported on this property.
+ * The special value {@code "*"} allows all headers to be exposed for
+ * non-credentialed requests.
* By default this is not set.
*/
public CorsRegistration exposedHeaders(String... headers) {
diff --git a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc.xsd b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc.xsd
index 0c0671c0c01..f6c66ff62b2 100644
--- a/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc.xsd
+++ b/spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc.xsd
@@ -1377,6 +1377,7 @@
Comma-separated list of response headers other than simple headers (i.e.
Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma) that an
actual response might have and can be exposed.
+ The special value "*" allows all headers to be exposed for non-credentialed requests.
Empty by default.
]]>