CorsConfiguration now supports pattern based origins.
Closes gh-24763
This commit is contained in:
committed by
Rossen Stoyanchev
parent
a1bab14140
commit
8632118e8d
@@ -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.
|
||||
@@ -45,6 +45,7 @@ import org.springframework.web.cors.CorsConfiguration;
|
||||
* @author Russell Allen
|
||||
* @author Sebastien Deleuze
|
||||
* @author Sam Brannen
|
||||
* @author Ruslan Akhundov
|
||||
* @since 4.2
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@@ -93,6 +94,23 @@ public @interface CrossOrigin {
|
||||
@AliasFor("value")
|
||||
String[] origins() default {};
|
||||
|
||||
/**
|
||||
* The list of allowed origins patterns that be specific origins, e.g.
|
||||
* {@code ".*\.domain1\.com"}, or {@code ".*"} for matching all origins.
|
||||
* <p>A matched origin is listed in the {@code Access-Control-Allow-Origin}
|
||||
* response header of preflight actual CORS requests.
|
||||
* <p>By default all origins are allowed.
|
||||
* <p><strong>Note:</strong> CORS checks use values from "Forwarded"
|
||||
* (<a href="https://tools.ietf.org/html/rfc7239">RFC 7239</a>),
|
||||
* "X-Forwarded-Host", "X-Forwarded-Port", and "X-Forwarded-Proto" headers,
|
||||
* if present, in order to reflect the client-originated address.
|
||||
* Consider using the {@code ForwardedHeaderFilter} in order to choose from a
|
||||
* central place whether to extract and use, or to discard such headers.
|
||||
* See the Spring Framework reference for more on this filter.
|
||||
* @see #value
|
||||
*/
|
||||
String[] originsPatterns() default {};
|
||||
|
||||
/**
|
||||
* The list of request headers that are permitted in actual requests,
|
||||
* possibly {@code "*"} to allow all headers.
|
||||
|
||||
@@ -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.
|
||||
@@ -23,6 +23,7 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.http.HttpMethod;
|
||||
@@ -45,6 +46,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @author Ruslan Akhundov
|
||||
* @since 4.2
|
||||
* @see <a href="https://www.w3.org/TR/cors/">CORS spec</a>
|
||||
*/
|
||||
@@ -52,6 +54,8 @@ public class CorsConfiguration {
|
||||
|
||||
/** Wildcard representing <em>all</em> origins, methods, or headers. */
|
||||
public static final String ALL = "*";
|
||||
/** Wildcard representing pattern that matches <em>all</em> origins. */
|
||||
public static final String ALL_PATTERN = ".*";
|
||||
|
||||
private static final List<HttpMethod> DEFAULT_METHODS = Collections.unmodifiableList(
|
||||
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD));
|
||||
@@ -62,10 +66,19 @@ public class CorsConfiguration {
|
||||
private static final List<String> DEFAULT_PERMIT_ALL = Collections.unmodifiableList(
|
||||
Collections.singletonList(ALL));
|
||||
|
||||
private static final List<String> DEFAULT_PERMIT_ALL_PATTERN_STR = Collections.unmodifiableList(
|
||||
Collections.singletonList(ALL_PATTERN));
|
||||
|
||||
private static final List<Pattern> DEFAULT_PERMIT_ALL_PATTERN = Collections.unmodifiableList(
|
||||
Collections.singletonList(Pattern.compile(ALL_PATTERN)));
|
||||
|
||||
|
||||
@Nullable
|
||||
private List<String> allowedOrigins;
|
||||
|
||||
@Nullable
|
||||
private List<Pattern> allowedOriginsPatterns;
|
||||
|
||||
@Nullable
|
||||
private List<String> allowedMethods;
|
||||
|
||||
@@ -99,6 +112,7 @@ public class CorsConfiguration {
|
||||
*/
|
||||
public CorsConfiguration(CorsConfiguration other) {
|
||||
this.allowedOrigins = other.allowedOrigins;
|
||||
this.allowedOriginsPatterns = other.allowedOriginsPatterns;
|
||||
this.allowedMethods = other.allowedMethods;
|
||||
this.resolvedMethods = other.resolvedMethods;
|
||||
this.allowedHeaders = other.allowedHeaders;
|
||||
@@ -140,6 +154,54 @@ public class CorsConfiguration {
|
||||
this.allowedOrigins.add(origin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the origins patterns to allow, e.g. {@code "*.com"}.
|
||||
* <p>By default this is not set.
|
||||
*/
|
||||
public CorsConfiguration setAllowedOriginsPatterns(@Nullable List<String> allowedOriginsPatterns) {
|
||||
if (allowedOriginsPatterns == null) {
|
||||
this.allowedOriginsPatterns = null;
|
||||
}
|
||||
else {
|
||||
this.allowedOriginsPatterns = new ArrayList<>(allowedOriginsPatterns.size());
|
||||
for (String pattern : allowedOriginsPatterns) {
|
||||
this.allowedOriginsPatterns.add(Pattern.compile(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured origins patterns to allow, or {@code null} if none.
|
||||
*
|
||||
* @see #addAllowedOriginPattern(String)
|
||||
* @see #setAllowedOriginsPatterns(List)
|
||||
*/
|
||||
@Nullable
|
||||
public List<String> getAllowedOriginsPatterns() {
|
||||
if (this.allowedOriginsPatterns == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.allowedOriginsPatterns == DEFAULT_PERMIT_ALL_PATTERN) {
|
||||
return DEFAULT_PERMIT_ALL_PATTERN_STR;
|
||||
}
|
||||
return this.allowedOriginsPatterns.stream().map(Pattern::toString).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an origin pattern to allow.
|
||||
*/
|
||||
public void addAllowedOriginPattern(String originPattern) {
|
||||
if (this.allowedOriginsPatterns == null) {
|
||||
this.allowedOriginsPatterns = new ArrayList<>(4);
|
||||
}
|
||||
else if (this.allowedOriginsPatterns == DEFAULT_PERMIT_ALL_PATTERN) {
|
||||
setAllowedOriginsPatterns(DEFAULT_PERMIT_ALL_PATTERN_STR);
|
||||
}
|
||||
this.allowedOriginsPatterns.add(Pattern.compile(originPattern));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"},
|
||||
* {@code "PUT"}, etc.
|
||||
@@ -351,7 +413,7 @@ public class CorsConfiguration {
|
||||
* </ul>
|
||||
*/
|
||||
public CorsConfiguration applyPermitDefaultValues() {
|
||||
if (this.allowedOrigins == null) {
|
||||
if (this.allowedOrigins == null && this.allowedOriginsPatterns == null) {
|
||||
this.allowedOrigins = DEFAULT_PERMIT_ALL;
|
||||
}
|
||||
if (this.allowedMethods == null) {
|
||||
@@ -392,7 +454,14 @@ public class CorsConfiguration {
|
||||
return this;
|
||||
}
|
||||
CorsConfiguration config = new CorsConfiguration(this);
|
||||
config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins()));
|
||||
List<String> combinedOrigins = combine(getAllowedOrigins(), other.getAllowedOrigins());
|
||||
List<String> combinedOriginsPatterns = combine(getAllowedOriginsPatterns(), other.getAllowedOriginsPatterns());
|
||||
if (combinedOrigins == DEFAULT_PERMIT_ALL && combinedOriginsPatterns != DEFAULT_PERMIT_ALL_PATTERN_STR
|
||||
&& !CollectionUtils.isEmpty(combinedOriginsPatterns)) {
|
||||
combinedOrigins = null;
|
||||
}
|
||||
config.setAllowedOrigins(combinedOrigins);
|
||||
config.setAllowedOriginsPatterns(combinedOriginsPatterns);
|
||||
config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods()));
|
||||
config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders()));
|
||||
config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders()));
|
||||
@@ -414,15 +483,20 @@ public class CorsConfiguration {
|
||||
if (source == null) {
|
||||
return other;
|
||||
}
|
||||
if (source == DEFAULT_PERMIT_ALL || source == DEFAULT_PERMIT_METHODS) {
|
||||
if (source == DEFAULT_PERMIT_ALL || source == DEFAULT_PERMIT_METHODS
|
||||
|| source == DEFAULT_PERMIT_ALL_PATTERN_STR) {
|
||||
return other;
|
||||
}
|
||||
if (other == DEFAULT_PERMIT_ALL || other == DEFAULT_PERMIT_METHODS) {
|
||||
if (other == DEFAULT_PERMIT_ALL || other == DEFAULT_PERMIT_METHODS
|
||||
|| other == DEFAULT_PERMIT_ALL_PATTERN_STR) {
|
||||
return source;
|
||||
}
|
||||
if (source.contains(ALL) || other.contains(ALL)) {
|
||||
return new ArrayList<>(Collections.singletonList(ALL));
|
||||
}
|
||||
if ( source.contains(ALL_PATTERN) || other.contains(ALL_PATTERN)) {
|
||||
return new ArrayList<>(Collections.singletonList(ALL_PATTERN));
|
||||
}
|
||||
Set<String> combined = new LinkedHashSet<>(source);
|
||||
combined.addAll(other);
|
||||
return new ArrayList<>(combined);
|
||||
@@ -439,21 +513,35 @@ public class CorsConfiguration {
|
||||
if (!StringUtils.hasText(requestOrigin)) {
|
||||
return null;
|
||||
}
|
||||
if (ObjectUtils.isEmpty(this.allowedOrigins)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.allowedOrigins.contains(ALL)) {
|
||||
if (this.allowCredentials != Boolean.TRUE) {
|
||||
return ALL;
|
||||
if (!ObjectUtils.isEmpty(this.allowedOrigins)) {
|
||||
if (this.allowedOrigins.contains(ALL)) {
|
||||
if (this.allowCredentials != Boolean.TRUE) {
|
||||
return ALL;
|
||||
}
|
||||
else {
|
||||
return requestOrigin;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return requestOrigin;
|
||||
for (String allowedOrigin : this.allowedOrigins) {
|
||||
if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
|
||||
return requestOrigin;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String allowedOrigin : this.allowedOrigins) {
|
||||
if (requestOrigin.equalsIgnoreCase(allowedOrigin)) {
|
||||
return requestOrigin;
|
||||
if (!ObjectUtils.isEmpty(this.allowedOriginsPatterns)) {
|
||||
for (Pattern allowedOriginsPattern : this.allowedOriginsPatterns) {
|
||||
if (allowedOriginsPattern.pattern().equals(ALL_PATTERN)) {
|
||||
if (this.allowCredentials != Boolean.TRUE) {
|
||||
return ALL;
|
||||
}
|
||||
else {
|
||||
return requestOrigin;
|
||||
}
|
||||
}
|
||||
else if (allowedOriginsPattern.matcher(requestOrigin).matches()) {
|
||||
return requestOrigin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user