Skip to content

Commit 8632118

Browse files
korekturrstoyanchev
authored andcommitted
CorsConfiguration now supports pattern based origins.
Closes gh-24763
1 parent a1bab14 commit 8632118

File tree

9 files changed

+308
-19
lines changed

9 files changed

+308
-19
lines changed

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

Lines changed: 19 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.
@@ -45,6 +45,7 @@
4545
* @author Russell Allen
4646
* @author Sebastien Deleuze
4747
* @author Sam Brannen
48+
* @author Ruslan Akhundov
4849
* @since 4.2
4950
*/
5051
@Target({ElementType.TYPE, ElementType.METHOD})
@@ -93,6 +94,23 @@
9394
@AliasFor("value")
9495
String[] origins() default {};
9596

97+
/**
98+
* The list of allowed origins patterns that be specific origins, e.g.
99+
* {@code ".*\.domain1\.com"}, or {@code ".*"} for matching all origins.
100+
* <p>A matched origin is listed in the {@code Access-Control-Allow-Origin}
101+
* response header of preflight actual CORS requests.
102+
* <p>By default all origins are allowed.
103+
* <p><strong>Note:</strong> CORS checks use values from "Forwarded"
104+
* (<a href="https://tools.ietf.org/html/rfc7239">RFC 7239</a>),
105+
* "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers,
106+
* if present, in order to reflect the client-originated address.
107+
* Consider using the {@code ForwardedHeaderFilter} in order to choose from a
108+
* central place whether to extract and use, or to discard such headers.
109+
* See the Spring Framework reference for more on this filter.
110+
* @see #value
111+
*/
112+
String[] originsPatterns() default {};
113+
96114
/**
97115
* The list of request headers that are permitted in actual requests,
98116
* possibly {@code "*"} to allow all headers.

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

Lines changed: 104 additions & 16 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.
@@ -23,6 +23,7 @@
2323
import java.util.LinkedHashSet;
2424
import java.util.List;
2525
import java.util.Set;
26+
import java.util.regex.Pattern;
2627
import java.util.stream.Collectors;
2728

2829
import org.springframework.http.HttpMethod;
@@ -45,13 +46,16 @@
4546
* @author Rossen Stoyanchev
4647
* @author Juergen Hoeller
4748
* @author Sam Brannen
49+
* @author Ruslan Akhundov
4850
* @since 4.2
4951
* @see <a href="https://www.w3.org/TR/cors/">CORS spec</a>
5052
*/
5153
public class CorsConfiguration {
5254

5355
/** Wildcard representing <em>all</em> origins, methods, or headers. */
5456
public static final String ALL = "*";
57+
/** Wildcard representing pattern that matches <em>all</em> origins. */
58+
public static final String ALL_PATTERN = ".*";
5559

5660
private static final List<HttpMethod> DEFAULT_METHODS = Collections.unmodifiableList(
5761
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD));
@@ -62,10 +66,19 @@ public class CorsConfiguration {
6266
private static final List<String> DEFAULT_PERMIT_ALL = Collections.unmodifiableList(
6367
Collections.singletonList(ALL));
6468

69+
private static final List<String> DEFAULT_PERMIT_ALL_PATTERN_STR = Collections.unmodifiableList(
70+
Collections.singletonList(ALL_PATTERN));
71+
72+
private static final List<Pattern> DEFAULT_PERMIT_ALL_PATTERN = Collections.unmodifiableList(
73+
Collections.singletonList(Pattern.compile(ALL_PATTERN)));
74+
6575

6676
@Nullable
6777
private List<String> allowedOrigins;
6878

79+
@Nullable
80+
private List<Pattern> allowedOriginsPatterns;
81+
6982
@Nullable
7083
private List<String> allowedMethods;
7184

@@ -99,6 +112,7 @@ public CorsConfiguration() {
99112
*/
100113
public CorsConfiguration(CorsConfiguration other) {
101114
this.allowedOrigins = other.allowedOrigins;
115+
this.allowedOriginsPatterns = other.allowedOriginsPatterns;
102116
this.allowedMethods = other.allowedMethods;
103117
this.resolvedMethods = other.resolvedMethods;
104118
this.allowedHeaders = other.allowedHeaders;
@@ -140,6 +154,54 @@ else if (this.allowedOrigins == DEFAULT_PERMIT_ALL) {
140154
this.allowedOrigins.add(origin);
141155
}
142156

157+
/**
158+
* Set the origins patterns to allow, e.g. {@code "*.com"}.
159+
* <p>By default this is not set.
160+
*/
161+
public CorsConfiguration setAllowedOriginsPatterns(@Nullable List<String> allowedOriginsPatterns) {
162+
if (allowedOriginsPatterns == null) {
163+
this.allowedOriginsPatterns = null;
164+
}
165+
else {
166+
this.allowedOriginsPatterns = new ArrayList<>(allowedOriginsPatterns.size());
167+
for (String pattern : allowedOriginsPatterns) {
168+
this.allowedOriginsPatterns.add(Pattern.compile(pattern));
169+
}
170+
}
171+
172+
return this;
173+
}
174+
175+
/**
176+
* Return the configured origins patterns to allow, or {@code null} if none.
177+
*
178+
* @see #addAllowedOriginPattern(String)
179+
* @see #setAllowedOriginsPatterns(List)
180+
*/
181+
@Nullable
182+
public List<String> getAllowedOriginsPatterns() {
183+
if (this.allowedOriginsPatterns == null) {
184+
return null;
185+
}
186+
if (this.allowedOriginsPatterns == DEFAULT_PERMIT_ALL_PATTERN) {
187+
return DEFAULT_PERMIT_ALL_PATTERN_STR;
188+
}
189+
return this.allowedOriginsPatterns.stream().map(Pattern::toString).collect(Collectors.toList());
190+
}
191+
192+
/**
193+
* Add an origin pattern to allow.
194+
*/
195+
public void addAllowedOriginPattern(String originPattern) {
196+
if (this.allowedOriginsPatterns == null) {
197+
this.allowedOriginsPatterns = new ArrayList<>(4);
198+
}
199+
else if (this.allowedOriginsPatterns == DEFAULT_PERMIT_ALL_PATTERN) {
200+
setAllowedOriginsPatterns(DEFAULT_PERMIT_ALL_PATTERN_STR);
201+
}
202+
this.allowedOriginsPatterns.add(Pattern.compile(originPattern));
203+
}
204+
143205
/**
144206
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"},
145207
* {@code "PUT"}, etc.
@@ -351,7 +413,7 @@ public Long getMaxAge() {
351413
* </ul>
352414
*/
353415
public CorsConfiguration applyPermitDefaultValues() {
354-
if (this.allowedOrigins == null) {
416+
if (this.allowedOrigins == null && this.allowedOriginsPatterns == null) {
355417
this.allowedOrigins = DEFAULT_PERMIT_ALL;
356418
}
357419
if (this.allowedMethods == null) {
@@ -392,7 +454,14 @@ public CorsConfiguration combine(@Nullable CorsConfiguration other) {
392454
return this;
393455
}
394456
CorsConfiguration config = new CorsConfiguration(this);
395-
config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins()));
457+
List<String> combinedOrigins = combine(getAllowedOrigins(), other.getAllowedOrigins());
458+
List<String> combinedOriginsPatterns = combine(getAllowedOriginsPatterns(), other.getAllowedOriginsPatterns());
459+
if (combinedOrigins == DEFAULT_PERMIT_ALL && combinedOriginsPatterns != DEFAULT_PERMIT_ALL_PATTERN_STR
460+
&& !CollectionUtils.isEmpty(combinedOriginsPatterns)) {
461+
combinedOrigins = null;
462+
}
463+
config.setAllowedOrigins(combinedOrigins);
464+
config.setAllowedOriginsPatterns(combinedOriginsPatterns);
396465
config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods()));
397466
config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders()));
398467
config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders()));
@@ -414,15 +483,20 @@ private List<String> combine(@Nullable List<String> source, @Nullable List<Strin
414483
if (source == null) {
415484
return other;
416485
}
417-
if (source == DEFAULT_PERMIT_ALL || source == DEFAULT_PERMIT_METHODS) {
486+
if (source == DEFAULT_PERMIT_ALL || source == DEFAULT_PERMIT_METHODS
487+
|| source == DEFAULT_PERMIT_ALL_PATTERN_STR) {
418488
return other;
419489
}
420-
if (other == DEFAULT_PERMIT_ALL || other == DEFAULT_PERMIT_METHODS) {
490+
if (other == DEFAULT_PERMIT_ALL || other == DEFAULT_PERMIT_METHODS
491+
|| other == DEFAULT_PERMIT_ALL_PATTERN_STR) {
421492
return source;
422493
}
423494
if (source.contains(ALL) || other.contains(ALL)) {
424495
return new ArrayList<>(Collections.singletonList(ALL));
425496
}
497+
if ( source.contains(ALL_PATTERN) || other.contains(ALL_PATTERN)) {
498+
return new ArrayList<>(Collections.singletonList(ALL_PATTERN));
499+
}
426500
Set<String> combined = new LinkedHashSet<>(source);
427501
combined.addAll(other);
428502
return new ArrayList<>(combined);
@@ -439,21 +513,35 @@ public String checkOrigin(@Nullable String requestOrigin) {
439513
if (!StringUtils.hasText(requestOrigin)) {
440514
return null;
441515
}
442-
if (ObjectUtils.isEmpty(this.allowedOrigins)) {
443-
return null;
444-
}
445516

446-
if (this.allowedOrigins.contains(ALL)) {
447-
if (this.allowCredentials != Boolean.TRUE) {
448-
return ALL;
517+
if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
518+
if (this.allowedOrigins.contains(ALL)) {
519+
if (this.allowCredentials != Boolean.TRUE) {
520+
return ALL;
521+
}
522+
else {
523+
return requestOrigin;
524+
}
449525
}
450-
else {
451-
return requestOrigin;
526+
for (String allowedOrigin : this.allowedOrigins) {
527+
if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
528+
return requestOrigin;
529+
}
452530
}
453531
}
454-
for (String allowedOrigin : this.allowedOrigins) {
455-
if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
456-
return requestOrigin;
532+
if (!ObjectUtils.isEmpty(this.allowedOriginsPatterns)) {
533+
for (Pattern allowedOriginsPattern : this.allowedOriginsPatterns) {
534+
if (allowedOriginsPattern.pattern().equals(ALL_PATTERN)) {
535+
if (this.allowCredentials != Boolean.TRUE) {
536+
return ALL;
537+
}
538+
else {
539+
return requestOrigin;
540+
}
541+
}
542+
else if (allowedOriginsPattern.matcher(requestOrigin).matches()) {
543+
return requestOrigin;
544+
}
457545
}
458546
}
459547

0 commit comments

Comments
 (0)