Browse Source

Allow "*" for Access-Control-Expose-Headers

Closes gh-26113
pull/23967/head
Rossen Stoyanchev 4 years ago
parent
commit
3e4ba75716
  1. 4
      spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java
  2. 14
      spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java
  3. 28
      spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java
  4. 5
      spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java
  5. 5
      spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java
  6. 1
      spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc.xsd

4
spring-web/src/main/java/org/springframework/web/bind/annotation/CrossOrigin.java

@ -1,5 +1,5 @@ @@ -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 { @@ -112,6 +112,8 @@ public @interface CrossOrigin {
* {@code Expires}, {@code Last-Modified}, or {@code Pragma},
* <p>Exposed headers are listed in the {@code Access-Control-Expose-Headers}
* response header of actual CORS requests.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* <p>By default no headers are listed as exposed.
*/
String[] exposedHeaders() default {};

14
spring-web/src/main/java/org/springframework/web/cors/CorsConfiguration.java

@ -1,5 +1,5 @@ @@ -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.
@ -253,13 +253,11 @@ public class CorsConfiguration { @@ -253,13 +253,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.
* <p>Note that {@code "*"} is not a valid exposed header value.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* <p>By default this is not set.
*/
public void setExposedHeaders(@Nullable List<String> exposedHeaders) {
if (exposedHeaders != null && exposedHeaders.contains(ALL)) {
throw new IllegalArgumentException("'*' is not a valid exposed header value");
}
this.exposedHeaders = (exposedHeaders != null ? new ArrayList<>(exposedHeaders) : null);
}
@ -275,12 +273,10 @@ public class CorsConfiguration { @@ -275,12 +273,10 @@ public class CorsConfiguration {
/**
* Add a response header to expose.
* <p>Note that {@code "*"} is not a valid exposed header value.
* <p>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);
}

28
spring-web/src/test/java/org/springframework/web/cors/CorsConfigurationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 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.
@ -60,27 +60,14 @@ public class CorsConfigurationTests { @@ -60,27 +60,14 @@ public class CorsConfigurationTests {
assertEquals(Arrays.asList("*"), config.getAllowedHeaders());
config.addAllowedMethod("*");
assertEquals(Arrays.asList("*"), config.getAllowedMethods());
config.addExposedHeader("header1");
config.addExposedHeader("header2");
assertEquals(Arrays.asList("header1", "header2"), config.getExposedHeaders());
config.addExposedHeader("*");
assertEquals(Arrays.asList("*"), config.getAllowedMethods());
config.setAllowCredentials(true);
assertTrue(config.getAllowCredentials());
config.setMaxAge(123L);
assertEquals(new Long(123), config.getMaxAge());
}
@Test(expected = IllegalArgumentException.class)
public void asteriskWildCardOnAddExposedHeader() {
CorsConfiguration config = new CorsConfiguration();
config.addExposedHeader("*");
}
@Test(expected = IllegalArgumentException.class)
public void asteriskWildCardOnSetExposedHeaders() {
CorsConfiguration config = new CorsConfiguration();
config.setExposedHeaders(Arrays.asList("*"));
}
@Test
public void combineWithNull() {
CorsConfiguration config = new CorsConfiguration();
@ -120,23 +107,27 @@ public class CorsConfigurationTests { @@ -120,23 +107,27 @@ public class CorsConfigurationTests {
assertEquals(Arrays.asList("https://domain.com"), combinedConfig.getAllowedOrigins());
assertEquals(Arrays.asList("header1"), combinedConfig.getAllowedHeaders());
assertEquals(Arrays.asList(HttpMethod.PUT.name()), combinedConfig.getAllowedMethods());
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
combinedConfig = other.combine(config);
assertEquals(Arrays.asList("https://domain.com"), combinedConfig.getAllowedOrigins());
assertEquals(Arrays.asList("header1"), combinedConfig.getAllowedHeaders());
assertEquals(Arrays.asList(HttpMethod.PUT.name()), combinedConfig.getAllowedMethods());
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
combinedConfig = config.combine(new CorsConfiguration());
assertEquals(Arrays.asList("*"), config.getAllowedOrigins());
assertEquals(Arrays.asList("*"), config.getAllowedHeaders());
assertEquals(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
HttpMethod.POST.name()), combinedConfig.getAllowedMethods());
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
combinedConfig = new CorsConfiguration().combine(config);
assertEquals(Arrays.asList("*"), config.getAllowedOrigins());
assertEquals(Arrays.asList("*"), config.getAllowedHeaders());
assertEquals(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
HttpMethod.POST.name()), combinedConfig.getAllowedMethods());
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
}
@Test
@ -144,19 +135,24 @@ public class CorsConfigurationTests { @@ -144,19 +135,24 @@ 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);
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedOrigins());
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedHeaders());
assertEquals(Arrays.asList("*"), combinedConfig.getExposedHeaders());
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedMethods());
combinedConfig = other.combine(config);
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedOrigins());
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedHeaders());
assertEquals(Arrays.asList("*"), combinedConfig.getExposedHeaders());
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedMethods());
}

5
spring-webflux/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java

@ -1,5 +1,5 @@ @@ -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 { @@ -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.
* <p>Note that {@code "*"} is not supported on this property.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* <p>By default this is not set.
*/
public CorsRegistration exposedHeaders(String... headers) {

5
spring-webmvc/src/main/java/org/springframework/web/servlet/config/annotation/CorsRegistration.java

@ -1,5 +1,5 @@ @@ -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 { @@ -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.
* <p>Note that {@code "*"} is not supported on this property.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* <p>By default this is not set.
*/
public CorsRegistration exposedHeaders(String... headers) {

1
spring-webmvc/src/main/resources/org/springframework/web/servlet/config/spring-mvc.xsd

@ -1363,6 +1363,7 @@ @@ -1363,6 +1363,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.
]]></xsd:documentation>
</xsd:annotation>

Loading…
Cancel
Save