Skip to content

Commit 3e4ba75

Browse files
committed
Allow "*" for Access-Control-Expose-Headers
Closes gh-26113
1 parent bd4ebd6 commit 3e4ba75

File tree

6 files changed

+27
-30
lines changed

6 files changed

+27
-30
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -112,6 +112,8 @@
112112
* {@code Expires}, {@code Last-Modified}, or {@code Pragma},
113113
* <p>Exposed headers are listed in the {@code Access-Control-Expose-Headers}
114114
* response header of actual CORS requests.
115+
* <p>The special value {@code "*"} allows all headers to be exposed for
116+
* non-credentialed requests.
115117
* <p>By default no headers are listed as exposed.
116118
*/
117119
String[] exposedHeaders() default {};

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

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -253,13 +253,11 @@ else if (this.allowedHeaders == DEFAULT_PERMIT_ALL) {
253253
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
254254
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}) that an
255255
* actual response might have and can be exposed.
256-
* <p>Note that {@code "*"} is not a valid exposed header value.
256+
* <p>The special value {@code "*"} allows all headers to be exposed for
257+
* non-credentialed requests.
257258
* <p>By default this is not set.
258259
*/
259260
public void setExposedHeaders(@Nullable List<String> exposedHeaders) {
260-
if (exposedHeaders != null && exposedHeaders.contains(ALL)) {
261-
throw new IllegalArgumentException("'*' is not a valid exposed header value");
262-
}
263261
this.exposedHeaders = (exposedHeaders != null ? new ArrayList<>(exposedHeaders) : null);
264262
}
265263

@@ -275,12 +273,10 @@ public List<String> getExposedHeaders() {
275273

276274
/**
277275
* Add a response header to expose.
278-
* <p>Note that {@code "*"} is not a valid exposed header value.
276+
* <p>The special value {@code "*"} allows all headers to be exposed for
277+
* non-credentialed requests.
279278
*/
280279
public void addExposedHeader(String exposedHeader) {
281-
if (ALL.equals(exposedHeader)) {
282-
throw new IllegalArgumentException("'*' is not a valid exposed header value");
283-
}
284280
if (this.exposedHeaders == null) {
285281
this.exposedHeaders = new ArrayList<>(4);
286282
}

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

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -60,27 +60,14 @@ public void setValues() {
6060
assertEquals(Arrays.asList("*"), config.getAllowedHeaders());
6161
config.addAllowedMethod("*");
6262
assertEquals(Arrays.asList("*"), config.getAllowedMethods());
63-
config.addExposedHeader("header1");
64-
config.addExposedHeader("header2");
65-
assertEquals(Arrays.asList("header1", "header2"), config.getExposedHeaders());
63+
config.addExposedHeader("*");
64+
assertEquals(Arrays.asList("*"), config.getAllowedMethods());
6665
config.setAllowCredentials(true);
6766
assertTrue(config.getAllowCredentials());
6867
config.setMaxAge(123L);
6968
assertEquals(new Long(123), config.getMaxAge());
7069
}
7170

72-
@Test(expected = IllegalArgumentException.class)
73-
public void asteriskWildCardOnAddExposedHeader() {
74-
CorsConfiguration config = new CorsConfiguration();
75-
config.addExposedHeader("*");
76-
}
77-
78-
@Test(expected = IllegalArgumentException.class)
79-
public void asteriskWildCardOnSetExposedHeaders() {
80-
CorsConfiguration config = new CorsConfiguration();
81-
config.setExposedHeaders(Arrays.asList("*"));
82-
}
83-
8471
@Test
8572
public void combineWithNull() {
8673
CorsConfiguration config = new CorsConfiguration();
@@ -120,43 +107,52 @@ public void combineWithDefaultPermitValues() {
120107
assertEquals(Arrays.asList("https://domain.com"), combinedConfig.getAllowedOrigins());
121108
assertEquals(Arrays.asList("header1"), combinedConfig.getAllowedHeaders());
122109
assertEquals(Arrays.asList(HttpMethod.PUT.name()), combinedConfig.getAllowedMethods());
110+
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
123111

124112
combinedConfig = other.combine(config);
125113
assertEquals(Arrays.asList("https://domain.com"), combinedConfig.getAllowedOrigins());
126114
assertEquals(Arrays.asList("header1"), combinedConfig.getAllowedHeaders());
127115
assertEquals(Arrays.asList(HttpMethod.PUT.name()), combinedConfig.getAllowedMethods());
116+
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
128117

129118
combinedConfig = config.combine(new CorsConfiguration());
130119
assertEquals(Arrays.asList("*"), config.getAllowedOrigins());
131120
assertEquals(Arrays.asList("*"), config.getAllowedHeaders());
132121
assertEquals(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
133122
HttpMethod.POST.name()), combinedConfig.getAllowedMethods());
123+
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
134124

135125
combinedConfig = new CorsConfiguration().combine(config);
136126
assertEquals(Arrays.asList("*"), config.getAllowedOrigins());
137127
assertEquals(Arrays.asList("*"), config.getAllowedHeaders());
138128
assertEquals(Arrays.asList(HttpMethod.GET.name(), HttpMethod.HEAD.name(),
139129
HttpMethod.POST.name()), combinedConfig.getAllowedMethods());
130+
assertEquals(Collections.emptyList(), combinedConfig.getExposedHeaders());
140131
}
141132

142133
@Test
143134
public void combineWithAsteriskWildCard() {
144135
CorsConfiguration config = new CorsConfiguration();
145136
config.addAllowedOrigin("*");
146137
config.addAllowedHeader("*");
138+
config.addExposedHeader("*");
147139
config.addAllowedMethod("*");
148140
CorsConfiguration other = new CorsConfiguration();
149141
other.addAllowedOrigin("https://domain.com");
150142
other.addAllowedHeader("header1");
151143
other.addExposedHeader("header2");
144+
other.addAllowedHeader("anotherHeader1");
145+
other.addExposedHeader("anotherHeader2");
152146
other.addAllowedMethod(HttpMethod.PUT.name());
153147
CorsConfiguration combinedConfig = config.combine(other);
154148
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedOrigins());
155149
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedHeaders());
150+
assertEquals(Arrays.asList("*"), combinedConfig.getExposedHeaders());
156151
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedMethods());
157152
combinedConfig = other.combine(config);
158153
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedOrigins());
159154
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedHeaders());
155+
assertEquals(Arrays.asList("*"), combinedConfig.getExposedHeaders());
160156
assertEquals(Arrays.asList("*"), combinedConfig.getAllowedMethods());
161157
}
162158

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -93,7 +93,8 @@ public CorsRegistration allowedHeaders(String... headers) {
9393
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
9494
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}, that an
9595
* actual response might have and can be exposed.
96-
* <p>Note that {@code "*"} is not supported on this property.
96+
* <p>The special value {@code "*"} allows all headers to be exposed for
97+
* non-credentialed requests.
9798
* <p>By default this is not set.
9899
*/
99100
public CorsRegistration exposedHeaders(String... headers) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -95,7 +95,8 @@ public CorsRegistration allowedHeaders(String... headers) {
9595
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
9696
* {@code Expires}, {@code Last-Modified}, or {@code Pragma}, that an
9797
* actual response might have and can be exposed.
98-
* <p>Note that {@code "*"} is not supported on this property.
98+
* <p>The special value {@code "*"} allows all headers to be exposed for
99+
* non-credentialed requests.
99100
* <p>By default this is not set.
100101
*/
101102
public CorsRegistration exposedHeaders(String... headers) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,7 @@
13631363
Comma-separated list of response headers other than simple headers (i.e.
13641364
Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, Pragma) that an
13651365
actual response might have and can be exposed.
1366+
The special value "*" allows all headers to be exposed for non-credentialed requests.
13661367
Empty by default.
13671368
]]></xsd:documentation>
13681369
</xsd:annotation>

0 commit comments

Comments
 (0)