257 lines
7.9 KiB
Plaintext
257 lines
7.9 KiB
Plaintext
[[webflux-cors]]
|
|
= CORS
|
|
[.small]#<<web.adoc#mvc-cors, Same as in Spring MVC>>#
|
|
|
|
Spring WebFlux lets you handle CORS (Cross-Origin Resource Sharing). This section
|
|
describes how to do so.
|
|
|
|
|
|
|
|
|
|
[[webflux-cors-intro]]
|
|
== Introduction
|
|
[.small]#<<web.adoc#mvc-cors-intro, Same as in Spring MVC>>#
|
|
|
|
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 -- for example, withdrawing money from your account!
|
|
|
|
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 lets you 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 as 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 see the specification for more details.
|
|
|
|
Spring WebFlux `HandlerMapping` implementations provide built-in support for CORS. After successfully
|
|
mapping a request to a handler, a `HandlerMapping` checks the CORS configuration for the
|
|
given request and handler and takes further actions. Preflight requests are handled
|
|
directly, while simple and actual CORS requests are intercepted, validated, and have the
|
|
required CORS response headers set.
|
|
|
|
In order to enable cross-origin requests (that is, 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
|
|
use the WebFlux Java configuration to declare such mappings, which results in a single,
|
|
global map passed to all `HadlerMappping` implementations.
|
|
|
|
You can combine global CORS configuration at the `HandlerMapping` level 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 -- for example,
|
|
all global and all local origins. For those attributes where only a single value can be
|
|
accepted, such as `allowCredentials` and `maxAge`, the local overrides the global value. See
|
|
{api-spring-framework}/web/cors/CorsConfiguration.html#combine-org.springframework.web.cors.CorsConfiguration-[`CorsConfiguration#combine(CorsConfiguration)`]
|
|
for more details.
|
|
|
|
[TIP]
|
|
====
|
|
To learn more from the source or to make advanced customizations, see:
|
|
|
|
* `CorsConfiguration`
|
|
* `CorsProcessor` and `DefaultCorsProcessor`
|
|
* `AbstractHandlerMapping`
|
|
====
|
|
|
|
|
|
|
|
|
|
[[webflux-cors-controller]]
|
|
== `@CrossOrigin`
|
|
[.small]#<<web.adoc#mvc-cors-controller, Same as in Spring MVC>>#
|
|
|
|
The {api-spring-framework}/web/bind/annotation/CrossOrigin.html[`@CrossOrigin`]
|
|
annotation enables cross-origin requests on annotated controller methods, as the
|
|
following example shows:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@RestController
|
|
@RequestMapping("/account")
|
|
public class AccountController {
|
|
|
|
@CrossOrigin
|
|
@GetMapping("/{id}")
|
|
public Mono<Account> retrieve(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
|
|
@DeleteMapping("/{id}")
|
|
public Mono<Void> remove(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
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 be used only where appropriate.
|
|
|
|
`maxAge` is set to 30 minutes.
|
|
|
|
`@CrossOrigin` is supported at the class level, too, and inherited by all methods.
|
|
The following example specifies a certain domain and sets `maxAge` to an hour:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@CrossOrigin(origins = "http://domain2.com", maxAge = 3600)
|
|
@RestController
|
|
@RequestMapping("/account")
|
|
public class AccountController {
|
|
|
|
@GetMapping("/{id}")
|
|
public Mono<Account> retrieve(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
|
|
@DeleteMapping("/{id}")
|
|
public Mono<Void> remove(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
You can use `@CrossOrigin` at both the class and the method level,
|
|
as the following example shows:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim,quotes"]
|
|
----
|
|
@CrossOrigin(maxAge = 3600) <1>
|
|
@RestController
|
|
@RequestMapping("/account")
|
|
public class AccountController {
|
|
|
|
@CrossOrigin("http://domain2.com") <2>
|
|
@GetMapping("/{id}")
|
|
public Mono<Account> retrieve(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
|
|
@DeleteMapping("/{id}")
|
|
public Mono<Void> remove(@PathVariable Long id) {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
<1> Using `@CrossOrigin` at the class level.
|
|
<2> Using `@CrossOrigin` at the method level.
|
|
|
|
|
|
|
|
[[webflux-cors-global]]
|
|
== Global Configuration
|
|
[.small]#<<web.adoc#mvc-cors-global, Same as in Spring MVC>>#
|
|
|
|
In addition to fine-grained, controller method-level configuration, you probably want to
|
|
define some global CORS configuration, too. You can set URL-based `CorsConfiguration`
|
|
mappings individually on any `HandlerMapping`. Most applications, however, use the
|
|
WebFlux Java configuration 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 be used only where appropriate.
|
|
|
|
`maxAge` is set to 30 minutes.
|
|
|
|
To enable CORS in the WebFlux Java configuration, you can use the `CorsRegistry` callback,
|
|
as the following example shows:
|
|
|
|
[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...
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
|
|
|
|
[[webflux-cors-webfilter]]
|
|
== CORS `WebFilter`
|
|
[.small]#<<web.adoc#mvc-cors-filter, Same as in Spring MVC>>#
|
|
|
|
You can apply CORS support through the built-in
|
|
{api-spring-framework}/web/cors/reactive/CorsWebFilter.html[`CorsWebFilter`], which is a
|
|
good fit with <<webflux-fn, functional endpoints>>.
|
|
|
|
NOTE: If you try 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, you can declare a `CorsWebFilter` bean and pass a
|
|
`CorsConfigurationSource` to its constructor, as the following example shows:
|
|
|
|
[source,java,indent=0]
|
|
[subs="verbatim"]
|
|
----
|
|
@Bean
|
|
CorsWebFilter corsFilter() {
|
|
|
|
CorsConfiguration config = new CorsConfiguration();
|
|
|
|
// Possibly...
|
|
// config.applyPermitDefaultValues()
|
|
|
|
config.setAllowCredentials(true);
|
|
config.addAllowedOrigin("http://domain1.com");
|
|
config.addAllowedHeader("*");
|
|
config.addAllowedMethod("*");
|
|
|
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
|
source.registerCorsConfiguration("/**", config);
|
|
|
|
return new CorsWebFilter(source);
|
|
}
|
|
----
|