Skip to content

Commit

Permalink
Refine CORS documentation for wildcard processing
Browse files Browse the repository at this point in the history
This commit refines CORS wildcard processing Javadoc to
provides more details on how wildcards are handled for
Access-Control-Allow-Methods, Access-Control-Allow-Headers
and Access-Control-Expose-Headers CORS headers.

For Access-Control-Expose-Headers, it is not possible to copy
the response headers which are not available at the point
when the CorsProcessor is invoked. Since all the major browsers
seem to support wildcard including on requests with credentials,
and since this is ultimately the user-agent responsibility to
check on client-side what is authorized or not, Spring Framework
continues to support this use case.

See spring-projectsgh-31143
  • Loading branch information
sdeleuze committed Sep 11, 2023
1 parent db76542 commit 76b8bb2
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 66 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -80,41 +80,32 @@

/**
* The list of request headers that are permitted in actual requests,
* possibly {@code "*"} to allow all headers.
* <p>Allowed headers are listed in the {@code Access-Control-Allow-Headers}
* response header of preflight requests.
* <p>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} as per the CORS spec.
* possibly {@code "*"} to allow all headers. Please, see
* {@link CorsConfiguration#setAllowedHeaders(List)} for details.
* <p>By default all requested headers are allowed.
*/
String[] allowedHeaders() default {};

/**
* The List of response headers that the user-agent will allow the client
* to access on an actual response, other than "simple" headers, i.e.
* {@code Cache-Control}, {@code Content-Language}, {@code Content-Type},
* {@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.
* to access on an actual response, possibly {@code "*"} to expose all headers.
* Please, see {@link CorsConfiguration#setExposedHeaders(List)} for details.
* <p>By default no headers are listed as exposed.
*/
String[] exposedHeaders() default {};

/**
* The list of supported HTTP request methods.
* The list of supported HTTP request methods. Please, see
* {@link CorsConfiguration#setAllowedMethods(List)} for details.
* <p>By default the supported methods are the same as the ones to which a
* controller method is mapped.
*/
RequestMethod[] methods() default {};

/**
* Whether the browser should send credentials, such as cookies along with
* cross domain requests, to the annotated endpoint. The configured value is
* set on the {@code Access-Control-Allow-Credentials} response header of
* preflight requests.
* cross domain requests, to the annotated endpoint. Please, see
* {@link CorsConfiguration#setAllowCredentials(Boolean)} for details.
* <p><strong>NOTE:</strong> Be aware that this option establishes a high
* level of trust with the configured domains and also increases the surface
* attack of the web application by exposing sensitive user-specific
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,12 @@ private static void parseCommaDelimitedOrigin(String rawValue, Consumer<String>

/**
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"},
* {@code "PUT"}, etc.
* <p>The special value {@code "*"} allows all methods.
* {@code "PUT"}, etc. The special value {@code "*"} allows all methods.
* <p>{@code Access-Control-Allow-Methods} response header is set either
* to the configured method or to {@code "*"}. Keep in mind however that the
* CORS spec does not allow {@code "*"} when {@link #setAllowCredentials
* allowCredentials} is set to {@code true}, that combination is handled
* by copying the method specified in the CORS preflight request.
* <p>If not set, only {@code "GET"} and {@code "HEAD"} are allowed.
* <p>By default this is not set.
* <p><strong>Note:</strong> CORS checks use values from "Forwarded"
Expand Down Expand Up @@ -312,24 +316,24 @@ public void setAllowedMethods(@Nullable List<String> allowedMethods) {
/**
* Return the allowed HTTP methods, or {@code null} in which case
* only {@code "GET"} and {@code "HEAD"} allowed.
* @see #setAllowedMethods(List)
* @see #addAllowedMethod(HttpMethod)
* @see #addAllowedMethod(String)
* @see #setAllowedMethods(List)
*/
@Nullable
public List<String> getAllowedMethods() {
return this.allowedMethods;
}

/**
* Add an HTTP method to allow.
* Variant of {@link #setAllowedMethods} for adding one allowed method at a time.
*/
public void addAllowedMethod(HttpMethod method) {
addAllowedMethod(method.name());
}

/**
* Add an HTTP method to allow.
* Variant of {@link #setAllowedMethods} for adding one allowed method at a time.
*/
public void addAllowedMethod(String method) {
if (StringUtils.hasText(method)) {
Expand All @@ -352,9 +356,13 @@ else if (this.resolvedMethods != null) {

/**
* Set the list of headers that a pre-flight request can list as allowed
* for use during an actual request.
* <p>The special value {@code "*"} allows actual requests to send any
* header.
* for use during an actual request. The special value {@code "*"} allows
* actual requests to send any header.
* <p>{@code Access-Control-Allow-Headers} response header is set either
* to the configured list of headers or to {@code "*"}. Keep in mind however
* that the CORS spec does not allow {@code "*"} when {@link #setAllowCredentials
* allowCredentials} is set to {@code true}, that combination is handled by
* copying the headers specified in the CORS preflight request.
* <p>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}.
Expand All @@ -375,7 +383,7 @@ public List<String> getAllowedHeaders() {
}

/**
* Add an actual request header to allow.
* Variant of {@link #setAllowedHeaders(List)} for adding one allowed header at a time.
*/
public void addAllowedHeader(String allowedHeader) {
if (this.allowedHeaders == null) {
Expand All @@ -388,12 +396,19 @@ else if (this.allowedHeaders == DEFAULT_PERMIT_ALL) {
}

/**
* 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
* actual response might have and can be exposed.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* Set the list of response headers that an actual response might have
* and can be exposed to the client. The special value {@code "*"}
* allows all headers to be exposed.
* <p>{@code Access-Control-Expose-Headers} response header is set either
* to the configured list of headers or to {@code "*"}. While the CORS
* spec does not allow {@code "*"} when {@code Access-Control-Allow-Credentials}
* is set to {@code true}, most browsers support it and
* the response headers are not all available during the CORS processing,
* so as a consequence {@code "*"} is the header value used when specified
* regardless of the value of the `allowCredentials` property.
* <p>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}.
* <p>By default this is not set.
*/
public void setExposedHeaders(@Nullable List<String> exposedHeaders) {
Expand All @@ -411,9 +426,7 @@ public List<String> getExposedHeaders() {
}

/**
* Add a response header to expose.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* Variant of {@link #setExposedHeaders} for adding one exposed header at a time.
*/
public void addExposedHeader(String exposedHeader) {
if (this.exposedHeaders == null) {
Expand All @@ -424,6 +437,15 @@ public void addExposedHeader(String exposedHeader) {

/**
* Whether user credentials are supported.
* <p>Setting this property has an impact on how {@link #setAllowedOrigins(List)
* origins}, {@link #setAllowedOriginPatterns(List) originPatterns},
* {@link #setAllowedMethods(List) allowedMethods} and
* {@link #setAllowedHeaders(List) allowedHeaders} are processed, see related
* API documentation for more details.
* <p><strong>NOTE:</strong> Be aware that this option establishes a high
* level of trust with the configured domains and also increases the surface
* attack of the web application by exposing sensitive user-specific
* information such as cookies and CSRF tokens.
* <p>By default this is not set (i.e. user credentials are not supported).
*/
public void setAllowCredentials(@Nullable Boolean allowCredentials) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private HttpMethod getMethodToUse(ServerHttpRequest request, boolean isPreFlight
/**
* Check the headers and determine the headers for the response of a
* pre-flight request. The default implementation simply delegates to
* {@link org.springframework.web.cors.CorsConfiguration#checkOrigin(String)}.
* {@link org.springframework.web.cors.CorsConfiguration#checkHeaders(List)}.
*/
@Nullable
protected List<String> checkHeaders(CorsConfiguration config, List<String> requestHeaders) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private HttpMethod getMethodToUse(ServerHttpRequest request, boolean isPreFlight
/**
* Check the headers and determine the headers for the response of a
* pre-flight request. The default implementation simply delegates to
* {@link CorsConfiguration#checkOrigin(String)}.
* {@link CorsConfiguration#checkHeaders(List)}.
*/
@Nullable
protected List<String> checkHeaders(CorsConfiguration config, List<String> requestHeaders) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -76,9 +76,11 @@ public CorsRegistration allowedOriginPatterns(String... patterns) {

/**
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, etc.
* <p>The special value {@code "*"} allows all methods.
* <p>By default "simple" methods {@code GET}, {@code HEAD}, and {@code POST}
* The special value {@code "*"} allows all methods. By default,
* "simple" methods {@code GET}, {@code HEAD}, and {@code POST}
* are allowed.
* <p>Please, see {@link CorsConfiguration#setAllowedMethods(List)} for
* details.
*/
public CorsRegistration allowedMethods(String... methods) {
this.config.setAllowedMethods(Arrays.asList(methods));
Expand All @@ -87,11 +89,10 @@ public CorsRegistration allowedMethods(String... methods) {

/**
* Set the list of headers that a pre-flight request can list as allowed
* for use during an actual request.
* <p>The special value {@code "*"} may be used to allow all headers.
* <p>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} as per the CORS spec.
* for use during an actual request. The special value {@code "*"}
* may be used to allow all headers.
* <p>Please, see {@link CorsConfiguration#setAllowedHeaders(List)} for
* details.
* <p>By default all headers are allowed.
*/
public CorsRegistration allowedHeaders(String... headers) {
Expand All @@ -100,12 +101,11 @@ public CorsRegistration allowedHeaders(String... headers) {
}

/**
* 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
* actual response might have and can be exposed.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* Set the list of response headers that an actual response might have and
* can be exposed. The special value {@code "*"} allows all headers to be
* exposed.
* <p>Please, see {@link CorsConfiguration#setExposedHeaders(List)} for
* details.
* <p>By default this is not set.
*/
public CorsRegistration exposedHeaders(String... headers) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
Expand Down Expand Up @@ -77,9 +77,11 @@ public CorsRegistration allowedOriginPatterns(String... patterns) {

/**
* Set the HTTP methods to allow, e.g. {@code "GET"}, {@code "POST"}, etc.
* <p>The special value {@code "*"} allows all methods.
* <p>By default "simple" methods {@code GET}, {@code HEAD}, and {@code POST}
* The special value {@code "*"} allows all methods. By default,
* "simple" methods {@code GET}, {@code HEAD}, and {@code POST}
* are allowed.
* <p>Please, see {@link CorsConfiguration#setAllowedMethods(List)} for
* details.
*/
public CorsRegistration allowedMethods(String... methods) {
this.config.setAllowedMethods(Arrays.asList(methods));
Expand All @@ -88,11 +90,10 @@ public CorsRegistration allowedMethods(String... methods) {

/**
* Set the list of headers that a pre-flight request can list as allowed
* for use during an actual request.
* <p>The special value {@code "*"} may be used to allow all headers.
* <p>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} as per the CORS spec.
* for use during an actual request. The special value {@code "*"}
* may be used to allow all headers.
* <p>Please, see {@link CorsConfiguration#setAllowedHeaders(List)} for
* details.
* <p>By default all headers are allowed.
*/
public CorsRegistration allowedHeaders(String... headers) {
Expand All @@ -101,12 +102,11 @@ public CorsRegistration allowedHeaders(String... headers) {
}

/**
* 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
* actual response might have and can be exposed.
* <p>The special value {@code "*"} allows all headers to be exposed for
* non-credentialed requests.
* Set the list of response headers that an actual response might have and
* can be exposed. The special value {@code "*"} allows all headers to be
* exposed.
* <p>Please, see {@link CorsConfiguration#setExposedHeaders(List)} for
* details.
* <p>By default this is not set.
*/
public CorsRegistration exposedHeaders(String... headers) {
Expand Down

0 comments on commit 76b8bb2

Please sign in to comment.