Spring MVC allows you to handle CORS (cross-origin resource sharing).
Introduction
For security reasons, browsers prohibit AJAX calls to resources outside the current origin. For example, you might have your bank account on one tab, and evil.com on another. Scripts from evil.com should not be able to make AJAX requests to your bank's API with your credentials - for example, withdraw money from your account!
Cross-origin resource sharing (CORS) is W3C specification implemented in most browsers, which allows you to determine which cross-domain requests are allowed, rather than using less secure and less powerful workarounds based on IFRAME or JSONP.
Processing
The CORS specification distinguishes between preflight, simple, and actual requests. To understand how CORS works, you can read this article. among other things, or refer to the specification for details.
The HandlerMapping
implementations from
Spring MVC provide native CORS support. After successfully mapping a request to a handler,
HandlerMapping
implementations check the CORS configuration for that request and handler and take
further action. Preliminary requests are processed directly, while simple and actual CORS requests are intercepted,
validated, and the required CORS response headers are set.
To allow requests between different origins (i.e.,
the Origin
header is present and different from the request host), you need to provide some explicitly
declared CORS configuration. If no suitable CORS configuration is found, preflight requests are rejected.
Responses to simple and actual CORS requests do not have CORS headers added and are therefore rejected by
browsers.
Each HandlerMapping
can be configured individually using CorsConfiguration
mappings based on URL
patterns. In
most cases, applications use a Java MVC configuration or XML namespace to declare such mappings, resulting in all
instances of HandlerMapping
being passed a single global Map.
You can combine global CORS
configuration at the level HandlerMapping
with more fine-grained CORS tuning at the handler level. For
example, annotated controllers may use @CrossOrigin
annotations at the class or method level (other
handlers may implement CorsConfigurationSource
).
The rules for combining global and local
configuration are typically additive - for example, all global and all local sources. For those attributes where
only one value can be accepted, such as allowCredentials
and maxAge
, the local value
overrides the global one. For more information, see the section on CorsConfiguration#combine(CorsConfiguration)
.
For more from the source or advanced configuration, check out the code:
CorsConfiguration
CorsProcessor
,DefaultCorsProcessor
AbstractHandlerMapping
@CrossOrigin
Annotation @CrossOrigin
allows queries between different sources to annotated controller
methods, as shown in the following example:
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
@RestController
@RequestMapping("/account")
class AccountController {
@CrossOrigin
@GetMapping("/{id}")
fun retrieve(@PathVariable id: Long): Account {
// ...
}
@DeleteMapping("/{id}")
fun remove(@PathVariable id: Long) {
// ...
}
}
By default, @CrossOrigin
allows:
All sources.
All headers.
All HTTP methods that the controller method is associated with.
allowCredentials
is not enabled by default, as this sets the trust level at which sensitive user
information (such as cookies and CSRF tokens) is disclosed, so it should only be used in in cases where it is really
necessary. If enabled, then either allowOrigins
must be set for one or more specific domains (but not
the special value "*"
), or alternatively the allowOriginPatterns property can be used to mappings
against a dynamic set of sources.
maxAge
is set to 30 minutes.
The
@CrossOrigin
annotation is supported at the class level and is also inherited all methods, as shown in the
following example:
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
@CrossOrigin(origins = ["https://domain2.com"], maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {
@GetMapping("/{id}")
fun retrieve(@PathVariable id: Long): Account {
// ...
}
@DeleteMapping("/{id}")
fun remove(@PathVariable id: Long) {
// ...
}
You can use the
@CrossOrigin
annotation at both the class and method level, as shown in the following example:
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
public class AccountController {
@CrossOrigin("https://domain2.com")
@GetMapping("/{id}")
public Account retrieve(@PathVariable Long id) {
// ...
}
@DeleteMapping("/{id}")
public void remove(@PathVariable Long id) {
// ...
}
}
@CrossOrigin(maxAge = 3600)
@RestController
@RequestMapping("/account")
class AccountController {
@CrossOrigin("https://domain2.com")
@GetMapping("/{id}")
fun retrieve(@PathVariable id: Long): Account {
// ...
}
@DeleteMapping("/{id}")
fun remove(@PathVariable id: Long) {
// ...
}
}
Global configuration
In addition to fine-tuning at the controller method level, you'll probably need
define the global CORS configuration. You can configure URL-based CorsConfiguration
mappings
individually for any HandlerMapping
. However, most applications use the MVC configuration in Java or
the MVC namespace in XML to do this.
By default, the global configuration enables the following:
All sources.
All headers.
Methods
GET
,HEAD
andPOST
.
allowCredentials
is not enabled by default because this sets the level of trust at which sensitive
user information (such as like cookies and CSRF tokens), so it should only be used in cases where it is really
necessary. If enabled, then either allowOrigins
must be set for one or more specific domains (but not
the special value "*"
), or alternatively the allowOriginPatterns property can be used to mappings
against a dynamic set of sources.
maxAge
is set to 30 minutes.
Java configuration
To enable CORS in Java configuration MVC, you can use the CorsRegistry
callback as shown in the
following example:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://domain2.com")
.allowedMethods("PUT", "DELETE")
.allowedHeaders(" header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600);
// Add more displays...
}
}
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun addCorsMappings(registry: CorsRegistry) {
registry.addMapping("/api/**")
.allowedOrigins("https://domain2.com")
.allowedMethods("PUT", "DELETE") .allowedHeaders("header1 ", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(true).maxAge(3600)
// Add more views...
}
}
XML Configuration
To enable CORS in an XML namespace, you can use the <mvc:cors>
element, as shown in the
following example :
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="https://domain1.com, https://domain2.com"
allowed-methods="GET, PUT"
allowed-headers="header1, header2, header3"
exposed-headers="header1, header2" allow-credentials="true"
max-age="123" />
<mvc:mapping path="/resources/**"
allowed-origins="https://domain1.com" />
</mvc:cors>
CORS filter
You can apply CORS support via the built-in CorsFilter
.
CorsFilter
with Spring Security, be aware
that Spring Security provides built-in CORS support.
To configure a filter, pass CorsConfigurationSource
to its constructor, as shown in the following
example:
CorsConfiguration config = new CorsConfiguration();
// Perhaps...
// config.applyPermitDefaultValues()
config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
CorsFilter filter = new CorsFilter(source);
val config = CorsConfiguration()
// Possibly...
// config.applyPermitDefaultValues()
config.allowCredentials = true config.addAllowedOrigin("https://domain1.com")
config.addAllowedHeader("*") config.addAllowedMethod("*")
val source = UrlBasedCorsConfigurationSource()
source.registerCorsConfiguration("/**", config)
val filter = CorsFilter(source)
GO TO FULL VERSION