From 72397e350551e0d6785608f7932aaad199b17fd2 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 19 Oct 2016 16:30:52 -0400 Subject: [PATCH] Polish Issue: SPR-14798 --- .../web/reactive/config/CorsRegistration.java | 52 +++--- .../RequestMappingHandlerMapping.java | 2 +- .../web/bind/annotation/CrossOrigin.java | 14 +- .../web/cors/CorsConfiguration.java | 164 ++++++++++-------- .../config/CorsBeanDefinitionParser.java | 4 +- .../config/annotation/CorsRegistration.java | 51 +++--- .../RequestMappingHandlerMapping.java | 2 +- 7 files changed, 154 insertions(+), 135 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java index e209a2628e..33bfca0b61 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/config/CorsRegistration.java @@ -23,12 +23,12 @@ import org.springframework.web.cors.CorsConfiguration; /** * Assists with the creation of a {@link CorsConfiguration} instance mapped to - * a path pattern. - * - *

By default, all origins, headers, credentials and {@code GET}, {@code HEAD}, and - * {@code POST} methods are allowed, while the max age is set to 30 minutes. + * a path pattern. By default all origins, headers, and credentials for + * {@code GET}, {@code HEAD}, and {@code POST} requests are allowed while the + * max age is set to 30 minutes. * * @author Sebastien Deleuze + * @author Rossen Stoyanchev * @since 5.0 * @see CorsRegistry */ @@ -40,24 +40,24 @@ public class CorsRegistration { /** - * Create a new {@link CorsRegistration} instance with all origins, headers, credentials - * and {@code GET}, {@code HEAD}, and {@code POST} methods allowed, and the max age - * set to 30 minutes on the specified path. - * @param pathPattern the path where the CORS configuration should apply. Exact path - * mapping URIs (such as {@code "/admin"}) are supported as well as Ant-style path - * patterns (such as {@code "/admin/**"}). + * Create a new {@link CorsRegistration} that allows all origins, headers, and + * credentials for {@code GET}, {@code HEAD}, and {@code POST} requests with + * max age set to 1800 seconds (30 minutes) for the specified path. + * + * @param pathPattern the path that the CORS configuration should apply to; + * exact path mapping URIs (such as {@code "/admin"}) are supported as well + * as Ant-style path patterns (such as {@code "/admin/**"}). */ public CorsRegistration(String pathPattern) { this.pathPattern = pathPattern; - // Same implicit default values as the @CrossOrigin annotation + allows simple methods - this.config = new CorsConfiguration().applyDefaultPermitConfiguration(); + this.config = new CorsConfiguration().applyPermitDefaultValues(); } /** * Set the origins to allow, e.g. {@code "http://domain1.com"}. *

The special value {@code "*"} allows all domains. - *

By default, all origins are allowed. + *

By default all origins are allowed. */ public CorsRegistration allowedOrigins(String... origins) { this.config.setAllowedOrigins(new ArrayList<>(Arrays.asList(origins))); @@ -65,10 +65,10 @@ public class CorsRegistration { } /** - * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, - * {@code "PUT"}, etc. + * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, etc. *

The special value {@code "*"} allows all methods. - *

By default, simple methods ({@code GET}, {@code HEAD}, and {@code POST}) are allowed. + *

By default "simple" methods {@code GET}, {@code HEAD}, and {@code POST} + * are allowed. */ public CorsRegistration allowedMethods(String... methods) { this.config.setAllowedMethods(new ArrayList<>(Arrays.asList(methods))); @@ -78,12 +78,11 @@ public class CorsRegistration { /** * Set the list of headers that a pre-flight request can list as allowed * for use during an actual request. - *

The special value {@code "*"} allows actual requests to send any - * header. + *

The special value {@code "*"} may be used to allow all headers. *

A header name is not required to be listed if it is one of: * {@code Cache-Control}, {@code Content-Language}, {@code Expires}, - * {@code Last-Modified}, or {@code Pragma}. - *

By default, all headers are allowed. + * {@code Last-Modified}, or {@code Pragma} as per the CORS spec. + *

By default all headers are allowed. */ public CorsRegistration allowedHeaders(String... headers) { this.config.setAllowedHeaders(new ArrayList<>(Arrays.asList(headers))); @@ -91,11 +90,11 @@ public class CorsRegistration { } /** - * Set the list of response headers other than simple headers (i.e. + * Set the list of response headers other than "simple" headers, i.e. * {@code Cache-Control}, {@code Content-Language}, {@code Content-Type}, - * {@code Expires}, {@code Last-Modified}, or {@code Pragma}) that an + * {@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. + *

Note that {@code "*"} is not supported on this property. *

By default this is not set. */ public CorsRegistration exposedHeaders(String... headers) { @@ -104,9 +103,9 @@ public class CorsRegistration { } /** - * Configure how long, in seconds, the response from a pre-flight request + * Configure how long in seconds the response from a pre-flight request * can be cached by clients. - *

By default, this is set to 1800 seconds (30 minutes). + *

By default this is set to 1800 seconds (30 minutes). */ public CorsRegistration maxAge(long maxAge) { this.config.setMaxAge(maxAge); @@ -115,7 +114,8 @@ public class CorsRegistration { /** * Whether user credentials are supported. - *

By default, this is set to {@code true} (i.e. user credentials are supported). + *

By default this is set to {@code true} in which case user credentials + * are supported. */ public CorsRegistration allowCredentials(boolean allowCredentials) { this.config.setAllowCredentials(allowCredentials); diff --git a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java index e0fdd20de7..547fadd451 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/reactive/result/method/annotation/RequestMappingHandlerMapping.java @@ -298,7 +298,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi config.addAllowedMethod(allowedMethod.name()); } } - return config.applyDefaultPermitConfiguration(); + return config.applyPermitDefaultValues(); } private void updateCorsConfig(CorsConfiguration config, CrossOrigin annotation) { 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 012c1e187c..34a061cd0d 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 @@ -28,8 +28,10 @@ import org.springframework.web.cors.CorsConfiguration; /** * Marks the annotated method or type as permitting cross origin requests. * - *

By default, all origins, headers are permitted, credentials are allowed and the - * maximum age is set to 30 minutes. + *

By default all origins and headers are permitted, credentials are allowed, + * and the maximum age is set to 1800 seconds (30 minutes). The list of HTTP + * methods is set to the methods on the {@code @RequestMapping} if not + * explicitly set on {@code @CrossOrigin}. * *

NOTE: {@code @CrossOrigin} is processed if an appropriate * {@code HandlerMapping}-{@code HandlerAdapter} pair is configured such as the @@ -47,25 +49,25 @@ import org.springframework.web.cors.CorsConfiguration; public @interface CrossOrigin { /** - * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyDefaultPermitConfiguration} + * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyPermitDefaultValues} */ @Deprecated String[] DEFAULT_ORIGINS = { "*" }; /** - * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyDefaultPermitConfiguration} + * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyPermitDefaultValues} */ @Deprecated String[] DEFAULT_ALLOWED_HEADERS = { "*" }; /** - * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyDefaultPermitConfiguration} + * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyPermitDefaultValues} */ @Deprecated boolean DEFAULT_ALLOW_CREDENTIALS = true; /** - * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyDefaultPermitConfiguration} + * @deprecated as of Spring 5.0, in favor of using {@link CorsConfiguration#applyPermitDefaultValues} */ @Deprecated long DEFAULT_MAX_AGE = 1800; 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 c4258833df..cdebc9db2a 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 @@ -29,8 +29,16 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; /** - * A container for CORS configuration that also provides methods to check - * the actual or requested origin, HTTP methods, and headers. + * A container for CORS configuration along with methods to check against the + * actual origin, HTTP methods, and headers of a given request. + * + *

By default a newly created {@code CorsConfiguration} does not permit any + * cross-origin requests and must be configured explicitly to indicate what + * should be allowed. + * + *

Use {@link #applyPermitDefaultValues()} to flip the initialization model + * to start with open defaults that permit all cross-origin requests for GET, + * HEAD, and POST requests. * * @author Sebastien Deleuze * @author Rossen Stoyanchev @@ -72,9 +80,9 @@ public class CorsConfiguration { /** - * Construct a new {@code CorsConfiguration} instance with nothing allowed by default - * but {@code "GET"} and {@code "HEAD"} methods. - * @see #applyDefaultPermitConfiguration() + * Construct a new {@code CorsConfiguration} instance with no cross-origin + * requests allowed for any origin by default. + * @see #applyPermitDefaultValues() */ public CorsConfiguration() { } @@ -93,75 +101,6 @@ public class CorsConfiguration { this.maxAge = other.maxAge; } - /** - * Apply the following default permit configuration only for properties that has not - * been defined: - *

- */ - public CorsConfiguration applyDefaultPermitConfiguration() { - if (this.allowedOrigins == null) { - this.addAllowedOrigin(ALL); - } - if (this.allowedMethods == null) { - this.setAllowedMethods(Arrays.asList(HttpMethod.GET.name(), - HttpMethod.HEAD.name(), HttpMethod.POST.name())); - } - if (this.allowedHeaders == null) { - this.addAllowedHeader(ALL); - } - if (this.allowCredentials == null) { - this.setAllowCredentials(true); - } - if (this.maxAge == null) { - this.setMaxAge(1800L); - } - return this; - } - - /** - * Combine the supplied {@code CorsConfiguration} with this one. - *

Properties of this configuration are overridden by any non-null - * properties of the supplied one. - * @return the combined {@code CorsConfiguration} or {@code this} - * configuration if the supplied configuration is {@code null} - */ - public CorsConfiguration combine(CorsConfiguration other) { - if (other == null) { - return this; - } - CorsConfiguration config = new CorsConfiguration(this); - config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins())); - config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods())); - config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders())); - config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders())); - Boolean allowCredentials = other.getAllowCredentials(); - if (allowCredentials != null) { - config.setAllowCredentials(allowCredentials); - } - Long maxAge = other.getMaxAge(); - if (maxAge != null) { - config.setMaxAge(maxAge); - } - return config; - } - - private List combine(List source, List other) { - if (other == null || other.contains(ALL)) { - return source; - } - if (source == null || source.contains(ALL)) { - return other; - } - Set combined = new LinkedHashSet<>(source); - combined.addAll(other); - return new ArrayList<>(combined); - } /** * Set the origins to allow, e.g. {@code "http://domain1.com"}. @@ -358,6 +297,83 @@ public class CorsConfiguration { return this.maxAge; } + /** + * By default a newly created {@code CorsConfiguration} does not permit any + * cross-origin requests and must be configured explicitly to indicate what + * should be allowed. + * + *

Use this method to flip the initialization model to start with open + * defaults that permit all cross-origin requests for GET, HEAD, and POST + * requests. Note however that this method will not override any existing + * values already set. + * + *

The following defaults are applied if not already set: + *

+ */ + public CorsConfiguration applyPermitDefaultValues() { + if (this.allowedOrigins == null) { + this.addAllowedOrigin(ALL); + } + if (this.allowedMethods == null) { + this.setAllowedMethods(Arrays.asList( + HttpMethod.GET.name(), HttpMethod.HEAD.name(), HttpMethod.POST.name())); + } + if (this.allowedHeaders == null) { + this.addAllowedHeader(ALL); + } + if (this.allowCredentials == null) { + this.setAllowCredentials(true); + } + if (this.maxAge == null) { + this.setMaxAge(1800L); + } + return this; + } + + /** + * Combine the supplied {@code CorsConfiguration} with this one. + *

Properties of this configuration are overridden by any non-null + * properties of the supplied one. + * @return the combined {@code CorsConfiguration} or {@code this} + * configuration if the supplied configuration is {@code null} + */ + public CorsConfiguration combine(CorsConfiguration other) { + if (other == null) { + return this; + } + CorsConfiguration config = new CorsConfiguration(this); + config.setAllowedOrigins(combine(getAllowedOrigins(), other.getAllowedOrigins())); + config.setAllowedMethods(combine(getAllowedMethods(), other.getAllowedMethods())); + config.setAllowedHeaders(combine(getAllowedHeaders(), other.getAllowedHeaders())); + config.setExposedHeaders(combine(getExposedHeaders(), other.getExposedHeaders())); + Boolean allowCredentials = other.getAllowCredentials(); + if (allowCredentials != null) { + config.setAllowCredentials(allowCredentials); + } + Long maxAge = other.getMaxAge(); + if (maxAge != null) { + config.setMaxAge(maxAge); + } + return config; + } + + private List combine(List source, List other) { + if (other == null || other.contains(ALL)) { + return source; + } + if (source == null || source.contains(ALL)) { + return other; + } + Set combined = new LinkedHashSet<>(source); + combined.addAll(other); + return new ArrayList<>(combined); + } /** * Check the origin of the request against the configured allowed origins. diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/CorsBeanDefinitionParser.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/CorsBeanDefinitionParser.java index a6354582d4..00f0e2278f 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/config/CorsBeanDefinitionParser.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/config/CorsBeanDefinitionParser.java @@ -48,7 +48,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser { List mappings = DomUtils.getChildElementsByTagName(element, "mapping"); if (mappings.isEmpty()) { - CorsConfiguration config = new CorsConfiguration().applyDefaultPermitConfiguration(); + CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues(); corsConfigurations.put("/**", config); } else { @@ -76,7 +76,7 @@ public class CorsBeanDefinitionParser implements BeanDefinitionParser { if (mapping.hasAttribute("max-age")) { config.setMaxAge(Long.parseLong(mapping.getAttribute("max-age"))); } - corsConfigurations.put(mapping.getAttribute("path"), config.applyDefaultPermitConfiguration()); + corsConfigurations.put(mapping.getAttribute("path"), config.applyPermitDefaultValues()); } } 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 bf39696141..495a216414 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 @@ -22,13 +22,13 @@ import java.util.Arrays; import org.springframework.web.cors.CorsConfiguration; /** - * {@code CorsRegistration} assists with the creation of a {@link CorsConfiguration} - * instance mapped to a path pattern. - * - *

By default, all origins, headers, credentials and {@code GET}, {@code HEAD}, and - * {@code POST} methods are allowed, and the max age is set to 30 minutes. + * Assists with the creation of a {@link CorsConfiguration} instance mapped to + * a path pattern. By default all origins, headers, and credentials for + * {@code GET}, {@code HEAD}, and {@code POST} requests are allowed while the + * max age is set to 30 minutes. * * @author Sebastien Deleuze + * @author Rossen Stoyanchev * @author Sam Brannen * @since 4.2 * @see CorsConfiguration @@ -42,17 +42,18 @@ public class CorsRegistration { /** - * Create a new {@link CorsRegistration} instance with all origins, headers, credentials - * and {@code GET}, {@code HEAD}, and {@code POST} methods allowed, and the max age - * set to 30 minutes on the specified path. - * @param pathPattern the path where the CORS configuration should apply. Exact path - * mapping URIs (such as {@code "/admin"}) are supported as well as Ant-style path - * patterns (such as {@code "/admin/**"}). + * Create a new {@link CorsRegistration} that allows all origins, headers, and + * credentials for {@code GET}, {@code HEAD}, and {@code POST} requests with + * max age set to 1800 seconds (30 minutes) for the specified path. + * + * @param pathPattern the path that the CORS configuration should apply to; + * exact path mapping URIs (such as {@code "/admin"}) are supported as well + * as Ant-style path patterns (such as {@code "/admin/**"}). */ public CorsRegistration(String pathPattern) { this.pathPattern = pathPattern; // Same implicit default values as the @CrossOrigin annotation + allows simple methods - this.config = new CorsConfiguration().applyDefaultPermitConfiguration(); + this.config = new CorsConfiguration().applyPermitDefaultValues(); } /** @@ -67,10 +68,10 @@ public class CorsRegistration { /** - * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, - * {@code "PUT"}, etc. + * Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, etc. *

The special value {@code "*"} allows all methods. - *

By default, simple methods ({@code GET}, {@code HEAD}, and {@code POST}) are allowed. + *

By default "simple" methods {@code GET}, {@code HEAD}, and {@code POST} + * are allowed. */ public CorsRegistration allowedMethods(String... methods) { this.config.setAllowedMethods(new ArrayList<>(Arrays.asList(methods))); @@ -80,12 +81,11 @@ public class CorsRegistration { /** * Set the list of headers that a pre-flight request can list as allowed * for use during an actual request. - *

The special value {@code "*"} allows actual requests to send any - * header. + *

The special value {@code "*"} may be used to allow all headers. *

A header name is not required to be listed if it is one of: * {@code Cache-Control}, {@code Content-Language}, {@code Expires}, - * {@code Last-Modified}, or {@code Pragma}. - *

By default, all headers are allowed. + * {@code Last-Modified}, or {@code Pragma} as per the CORS spec. + *

By default all headers are allowed. */ public CorsRegistration allowedHeaders(String... headers) { this.config.setAllowedHeaders(new ArrayList<>(Arrays.asList(headers))); @@ -93,11 +93,11 @@ public class CorsRegistration { } /** - * Set the list of response headers other than simple headers (i.e. + * Set the list of response headers other than "simple" headers, i.e. * {@code Cache-Control}, {@code Content-Language}, {@code Content-Type}, - * {@code Expires}, {@code Last-Modified}, or {@code Pragma}) that an + * {@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. + *

Note that {@code "*"} is not supported on this property. *

By default this is not set. */ public CorsRegistration exposedHeaders(String... headers) { @@ -106,9 +106,9 @@ public class CorsRegistration { } /** - * Configure how long, in seconds, the response from a pre-flight request + * Configure how long in seconds the response from a pre-flight request * can be cached by clients. - *

By default, this is set to 1800 seconds (30 minutes). + *

By default this is set to 1800 seconds (30 minutes). */ public CorsRegistration maxAge(long maxAge) { this.config.setMaxAge(maxAge); @@ -117,7 +117,8 @@ public class CorsRegistration { /** * Whether user credentials are supported. - *

By default, this is set to {@code true} (i.e. user credentials are supported). + *

By default this is set to {@code true} in which case user credentials + * are supported. */ public CorsRegistration allowCredentials(boolean allowCredentials) { this.config.setAllowCredentials(allowCredentials); diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java index 7f0c24f493..6d144bf18b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java @@ -309,7 +309,7 @@ public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMappi config.addAllowedMethod(allowedMethod.name()); } } - return config.applyDefaultPermitConfiguration(); + return config.applyPermitDefaultValues(); } private void updateCorsConfig(CorsConfiguration config, CrossOrigin annotation) {