Update CORS chapters

- Add "Processing" section (also replaces Advanced Customizations)
- Add information on out-of-the-box behavior
- Add more deails on @CrossOririn default configuratio
- Add cross-references between Spring MVC and WebFlux
- Polish
This commit is contained in:
Rossen Stoyanchev 2017-11-28 16:27:08 -05:00
parent 375a63a041
commit bec63fbb33
2 changed files with 208 additions and 197 deletions

View File

@ -1,45 +1,79 @@
[[webflux-cors]]
= CORS
[.small]#<<web.adoc#mvc-cors,Same in Spring MVC>>#
[[webflux-cors-intro]]
== Introduction
[.small]#<<web.adoc#mvc-cors-intro,Same in Spring MVC>>#
For security reasons, browsers prohibit AJAX calls to resources residing outside the
current origin. For example, as you're checking your bank account in one tab, you
could have the evil.com website open in another tab. The scripts from evil.com should not
be able to make AJAX requests to your bank API (e.g., withdrawing money from your account!)
using your credentials.
For security reasons browsers prohibit AJAX calls to resources outside the current origin.
For example you could have your bank account in one tab and evil.com in another. Scripts
from evil.com should not be able to make AJAX requests to your bank API with your
credentials, e.g. withdrawing money from your account!
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross-origin resource sharing]
(CORS) is a http://www.w3.org/TR/cors/[W3C specification] implemented by
http://caniuse.com/#feat=cors[most browsers] that allows you to specify in a flexible
way what kind of cross domain requests are authorized, instead of using some less secured
and less powerful hacks like IFRAME or JSONP.
Cross-Origin Resource Sharing (CORS) is a http://www.w3.org/TR/cors/[W3C specification]
implemented by http://caniuse.com/#feat=cors[most browsers] that allows you to specify
what kind of cross domain requests are authorized rather than using less secure and less
powerful workarounds based on IFRAME or JSONP.
[[webflux-cors-processing]]
== Processing
[.small]#<<web.adoc#mvc-cors-processing,Same in Spring MVC>>#
The CORS specification distinguishes between preflight, simple, and actual requests.
To learn how CORS works, you can read
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among
many others, or refer to the specification for more details.
Spring WebFlux ``HandlerMapping``'s provide built-in support for CORS. After successfully
mapping a request to a handler, ``HandlerMapping``'s check the CORS configuration for the
given request and handler and take further actions. Preflight requests are handled
directly while simple and actual CORS requests are intercepted, validated, and have
required CORS response headers set.
In order to enable cross-origin requests (i.e. the `Origin` header is present and
differs from the host of the request) you need to have some explicitly declared CORS
configuration. If no matching CORS configuration is found, preflight requests are
rejected. No CORS headers are added to the responses of simple and actual CORS requests
and consequently browsers reject them.
Each `HandlerMapping` can be
{api-spring-framework}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
individually with URL pattern based `CorsConfiguration` mappings. In most cases applications
will use the WebFlux Java config to declare such mappings, which results in a single,
global map passed to all ``HadlerMappping``'s.
Global CORS configuration at the `HandlerMapping` level can be combined with more
fine-grained, handler-level CORS configuration. For example annotated controllers can use
class or method-level `@CrossOrigin` annotations (other handlers can implement
`CorsConfigurationSource`).
The rules for combining global and local configuration are generally additive -- e.g.
all global and all local origins. The only exception are those attributes where only a
single value can be accepted such as `allowCredentials` and `maxAge`, in which case the
local overrides the global value.
To learn more from the source or make advanced customizations, check:
* `CorsConfiguration`
* `CorsProcessor`, `DefaultCorsProcessor`
* `AbstractHandlerMapping`
Spring WebFlux supports CORS out of the box. CORS requests, including preflight ones with an `OPTIONS` method,
are automatically dispatched to the various registered ``HandlerMapping``s. They handle
CORS preflight requests and intercept CORS simple and actual requests thanks to a
{api-spring-framework}/web/cors/reactive/CorsProcessor.html[CorsProcessor]
implementation (https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/cors/reactive/DefaultCorsProcessor.java[DefaultCorsProcessor]
by default) in order to add the relevant CORS response headers (like `Access-Control-Allow-Origin`)
based on the CORS configuration you have provided.
[NOTE]
====
Be aware that cookies are not allowed by default to avoid increasing the surface attack of
the web application (for example via exposing sensitive user-specific information like
CSRF tokens). Set `allowedCredentials` property to `true` in order to allow them.
====
[[webflux-cors-controller]]
== @CrossOrigin
[.small]#<<web.adoc#mvc-cors-controller,Same in Spring MVC>>#
You can add an
{api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation to your `@RequestMapping` annotated handler method in order to enable CORS on
it. By default `@CrossOrigin` allows all origins and the HTTP methods specified in the
`@RequestMapping` annotation:
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation enables cross-origin requests on annotated controller methods:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -61,7 +95,17 @@ public class AccountController {
}
----
It is also possible to enable CORS for the whole controller:
By default `@CrossOrigin` allows:
* All origins.
* All headers.
* All HTTP methods to which the controller method is mapped.
* `allowedCredentials` is not enabled by default since that establishes a trust level
that exposes sensitive user-specific information such as cookies and CSRF tokens, and
should only be used where appropriate.
* `maxAge` is set to 30 minutes.
`@CrossOrigin` is supported at the class level too and inherited by all methods:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -83,12 +127,7 @@ public class AccountController {
}
----
In the above example CORS support is enabled for both the `retrieve()` and the `remove()`
handler methods, and you can also see how you can customize the CORS configuration using
`@CrossOrigin` attributes.
You can even use both controller-level and method-level CORS configurations; Spring will
then combine attributes from both annotations to create merged CORS configuration.
`CrossOrigin` can be used at both class and method-level:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -100,27 +139,40 @@ public class AccountController {
@CrossOrigin("http://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
public Mono<Account> retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
public Mono<Void> remove(@PathVariable Long id) {
// ...
}
}
----
[[webflux-cors-java-config]]
== Java Config
In addition to fine-grained, annotation-based configuration you'll probably want to
define some global CORS configuration as well. This is similar to using filters but can
be declared within Spring WebFlux and combined with fine-grained `@CrossOrigin` configuration.
By default all origins and `GET`, `HEAD`, and `POST` methods are allowed.
Enabling CORS for the whole application is as simple as:
[[webflux-cors-global]]
== Global Config
[.small]#<<web.adoc#mvc-cors-global,Same in Spring MVC>>#
In addition to fine-grained, controller method level configuration you'll probably want to
define some global CORS configuration too. You can set URL-based `CorsConfiguration`
mappings individually on any `HandlerMapping`. Most applications however will use the
WebFlux Java config to do that.
By default global configuration enables the following:
* All origins.
* All headers.
* `GET`, `HEAD`, and `POST` methods.
* `allowedCredentials` is not enabled by default since that establishes a trust level
that exposes sensitive user-specific information such as cookies and CSRF tokens, and
should only be used where appropriate.
* `maxAge` is set to 30 minutes.
To enable CORS in the WebFlux Java config, use the `CorsRegistry` callback:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -131,29 +183,15 @@ public class WebConfig implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
----
You can easily change any properties, as well as only apply this CORS configuration to a
specific path pattern:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Configuration
@EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
// Add more mappings...
}
}
----
@ -161,6 +199,7 @@ public class WebConfig implements WebFluxConfigurer {
[[webflux-cors-webfilter]]
== CORS WebFilter
[.small]#<<web.adoc#mvc-cors-filter,Same in Spring MVC>>#
You can apply CORS support through the built-in
{api-spring-framework}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a
@ -170,10 +209,16 @@ To configure the filter, you can declare a `CorsWebFilter` bean and pass a
`CorsConfigurationSource` to its constructor:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Bean
CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
// Possibly...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
@ -186,29 +231,3 @@ CorsWebFilter corsFilter() {
}
----
You can also easily permit all cross-origin requests for GET, HEAD, and POST requests by writing
[source,java,indent=0]
----
@Bean
CorsWebFilter corsFilter() {
return new CorsWebFilter(exchange -> new CorsConfiguration().applyPermitDefaultValues());
}
----
[[webflux-cors-customizations]]
== Advanced Customization
{api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
allows you to specify how the CORS requests should be processed: allowed origins, headers, methods, etc.
It can be provided in various ways:
* {api-spring-framework}/web/reactive/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[`AbstractHandlerMapping#setCorsConfigurations()`]
allows to specify a `Map` with several {api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
instances mapped to path patterns like `/api/**`.
* Subclasses can provide their own `CorsConfiguration` by overriding the
`AbstractHandlerMapping#getCorsConfiguration(Object, ServerWebExchange)` method.
* Handlers can implement the {api-spring-framework}/web/cors/reactive/CorsConfigurationSource.html[`CorsConfigurationSource`]
interface in order to provide a {api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
instance for each request.

View File

@ -1,57 +1,79 @@
[[mvc-cors]]
= CORS
[.small]#<<web-reactive.adoc#webflux-cors,Same in Spring WebFlux>>#
[[mvc-cors-intro]]
== Introduction
[.small]#<<web-reactive.adoc#webflux-cors-intro,Same in Spring WebFlux>>#
For security reasons, browsers prohibit AJAX calls to resources residing outside the
current origin. For example, as you're checking your bank account in one tab, you
could have the evil.com website open in another tab. The scripts from evil.com should not
be able to make AJAX requests to your bank API (e.g., withdrawing money from your account!)
using your credentials.
For security reasons browsers prohibit AJAX calls to resources outside the current origin.
For example you could have your bank account in one tab and evil.com in another. Scripts
from evil.com should not be able to make AJAX requests to your bank API with your
credentials, e.g. withdrawing money from your account!
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing[Cross-origin resource sharing]
(CORS) is a http://www.w3.org/TR/cors/[W3C specification] implemented by
http://caniuse.com/#feat=cors[most browsers] that allows you to specify in a flexible
way what kind of cross domain requests are authorized, instead of using some less secured
and less powerful hacks like IFRAME or JSONP.
Cross-Origin Resource Sharing (CORS) is a http://www.w3.org/TR/cors/[W3C specification]
implemented by http://caniuse.com/#feat=cors[most browsers] that allows you to specify
what kind of cross domain requests are authorized rather than using less secure and less
powerful workarounds based on IFRAME or JSONP.
As of Spring Framework 4.2, CORS is supported out of the box. CORS requests
(https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/FrameworkServlet.java#L906[including preflight ones with an `OPTIONS` method])
are automatically dispatched to the various registered ``HandlerMapping``s. They handle
CORS preflight requests and intercept CORS simple and actual requests thanks to a
{api-spring-framework}/web/cors/CorsProcessor.html[CorsProcessor]
implementation (https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/cors/DefaultCorsProcessor.java[DefaultCorsProcessor]
by default) in order to add the relevant CORS response headers (like `Access-Control-Allow-Origin`)
based on the CORS configuration you have provided.
[NOTE]
====
Be aware that cookies are not allowed by default to avoid increasing the surface attack of
the web application (for example via exposing sensitive user-specific information like
CSRF tokens). Set `allowedCredentials` property to `true` in order to allow them.
====
[NOTE]
====
Since CORS requests are automatically dispatched, you *do not need* to change the
`DispatcherServlet` `dispatchOptionsRequest` init parameter value; using its default value
(`false`) is the recommended approach.
====
[[mvc-cors-processing]]
== Processing
[.small]#<<web-reactive.adoc#webflux-cors-processing,Same in Spring WebFlux>>#
The CORS specification distinguishes between preflight, simple, and actual requests.
To learn how CORS works, you can read
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS[this article], among
many others, or refer to the specification for more details.
Spring MVC ``HandlerMapping``'s provide built-in support for CORS. After successfully
mapping a request to a handler, ``HandlerMapping``'s check the CORS configuration for the
given request and handler and take further actions. Preflight requests are handled
directly while simple and actual CORS requests are intercepted, validated, and have
required CORS response headers set.
In order to enable cross-origin requests (i.e. the `Origin` header is present and
differs from the host of the request) you need to have some explicitly declared CORS
configuration. If no matching CORS configuration is found, preflight requests are
rejected. No CORS headers are added to the responses of simple and actual CORS requests
and consequently browsers reject them.
Each `HandlerMapping` can be
{api-spring-framework}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[configured]
individually with URL pattern based `CorsConfiguration` mappings. In most cases applications
will use the MVC Java config or the XML namespace to declare such mappings, which results
in a single, global map passed to all ``HadlerMappping``'s.
Global CORS configuration at the `HandlerMapping` level can be combined with more
fine-grained, handler-level CORS configuration. For example annotated controllers can use
class or method-level `@CrossOrigin` annotations (other handlers can implement
`CorsConfigurationSource`).
The rules for combining global and local configuration are generally additive -- e.g.
all global and all local origins. The only exception are those attributes where only a
single value can be accepted such as `allowCredentials` and `maxAge`, in which case the
local overrides the global value.
To learn more from the source or make advanced customizations, check:
* `CorsConfiguration`
* `CorsProcessor`, `DefaultCorsProcessor`
* `AbstractHandlerMapping`
[[mvc-cors-controller]]
== @CrossOrigin
[.small]#<<web-reactive.adoc#webflux-cors-controller,Same in Spring WebFlux>>#
You can add an
{api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation to your `@RequestMapping` annotated handler method in order to enable CORS on
it. By default `@CrossOrigin` allows all origins and the HTTP methods specified in the
`@RequestMapping` annotation:
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
annotation enables cross-origin requests on annotated controller methods:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -73,7 +95,17 @@ public class AccountController {
}
----
It is also possible to enable CORS for the whole controller:
By default `@CrossOrigin` allows:
* All origins.
* All headers.
* All HTTP methods to which the controller method is mapped.
* `allowedCredentials` is not enabled by default since that establishes a trust level
that exposes sensitive user-specific information such as cookies and CSRF tokens, and
should only be used where appropriate.
* `maxAge` is set to 30 minutes.
`@CrossOrigin` is supported at the class level too and inherited by all methods:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -95,12 +127,7 @@ public class AccountController {
}
----
In the above example CORS support is enabled for both the `retrieve()` and the `remove()`
handler methods, and you can also see how you can customize the CORS configuration using
`@CrossOrigin` attributes.
You can even use both controller-level and method-level CORS configurations; Spring will
then combine attributes from both annotations to create merged CORS configuration.
`CrossOrigin` can be used at both class and method-level:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -111,12 +138,12 @@ then combine attributes from both annotations to create merged CORS configuratio
public class AccountController {
@CrossOrigin("http://domain2.com")
@RequestMapping("/{id}")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
@ -127,19 +154,32 @@ public class AccountController {
[[mvc-cors-global]]
== Global CORS
== Global Config
[.small]#<<web-reactive.adoc#webflux-cors-global,Same in Spring WebFlux>>#
In addition to fine-grained, controller method level configuration you'll probably want to
define some global CORS configuration too. You can set URL-based `CorsConfiguration`
mappings individually on any `HandlerMapping`. Most applications however will use the
MVC Java config or the MVC XNM namespace to do that.
By default global configuration enables the following:
* All origins.
* All headers.
* `GET`, `HEAD`, and `POST` methods.
* `allowedCredentials` is not enabled by default since that establishes a trust level
that exposes sensitive user-specific information such as cookies and CSRF tokens, and
should only be used where appropriate.
* `maxAge` is set to 30 minutes.
In addition to fine-grained, annotation-based configuration you'll probably want to
define some global CORS configuration as well. This is similar to using filters but can
be declared within Spring MVC and combined with fine-grained `@CrossOrigin` configuration.
By default all origins and `GET`, `HEAD`, and `POST` methods are allowed.
[[mvc-cors-global-java]]
=== Java Config
[.small]#<<web-reactive.adoc#webflux-cors-global,Same in Spring WebFlux>>#
Enabling CORS for the whole application is as simple as:
To enable CORS in the MVC Java config, use the `CorsRegistry` callback:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -150,29 +190,15 @@ public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
----
You can easily change any properties, as well as only apply this CORS configuration to a
specific path pattern:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
// Add more mappings...
}
}
----
@ -182,18 +208,7 @@ public class WebConfig implements WebMvcConfigurer {
[[mvc-cors-global-xml]]
=== XML Config
The following minimal XML configuration enables CORS for the `/**` path pattern with
the same default properties as with the aforementioned JavaConfig examples:
[source,xml,indent=0]
[subs="verbatim"]
----
<mvc:cors>
<mvc:mapping path="/**" />
</mvc:cors>
----
It is also possible to declare several CORS mappings with customized properties:
To enable CORS in the XML namespace, use the `<mvc:cors>` element:
[source,xml,indent=0]
[subs="verbatim"]
@ -216,45 +231,32 @@ It is also possible to declare several CORS mappings with customized properties:
[[mvc-cors-customizations]]
== Advanced Customization
{api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
allows you to specify how the CORS requests should be processed: allowed origins, headers, methods, etc.
It can be provided in various ways:
* {api-spring-framework}/web/servlet/handler/AbstractHandlerMapping.html#setCorsConfigurations-java.util.Map-[`AbstractHandlerMapping#setCorsConfigurations()`]
allows to specify a `Map` with several {api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
instances mapped to path patterns like `/api/**`.
* Subclasses can provide their own `CorsConfiguration` by overriding the
`AbstractHandlerMapping#getCorsConfiguration(Object, HttpServletRequest)` method.
* Handlers can implement the {api-spring-framework}/web/cors/CorsConfigurationSource.html[`CorsConfigurationSource`]
interface (like https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java[`ResourceHttpRequestHandler`]
now does) in order to provide a {api-spring-framework}/web/cors/CorsConfiguration.html[CorsConfiguration]
instance for each request.
[[mvc-cors-filter]]
== CORS Filter
[.small]#<<web-reactive.adoc#webflux-cors-webfilter,Same in Spring WebFlux>>#
You can apply CORS support through the built-in
{api-spring-framework}/web/filter/CorsFilter.html[`CorsFilter`].
[NOTE]
====
Spring Security now provides
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors[builtin support for CORS]
so you don't need to use a `CorsFilter`.
If you're trying to use the `CorsFilter` with Spring Security, keep in mind that Spring
Security has
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors[built-in support]
for CORS.
====
To configure the filter pass a
`CorsConfigurationSource` to its constructor:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
CorsConfiguration config = new CorsConfiguration();
// Possibly...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("http://domain1.com");
config.addAllowedHeader("*");
@ -266,13 +268,3 @@ source.registerCorsConfiguration("/**", config);
CorsFilter filter = new CorsFilter(source);
----
You can also easily permit all cross-origin requests for GET, HEAD, and POST requests by writing
[source,java,indent=0]
----
CorsFilter filter = new CorsFilter(exchange -> new CorsConfiguration().applyPermitDefaultValues());
----
Also the information on
https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors[CORS]
in the Spring Security reference.