CORS

Module 5. Spring
Level 9 , Lesson 12
Available

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:

Java

@RestController
@RequestMapping("/account")
public class AccountController {
    @CrossOrigin
    @GetMapping("/{id}")
    public Account retrieve(@PathVariable Long id) {
        // ...
    }
    @DeleteMapping("/{id}")
    public void remove(@PathVariable Long id) {
        // ...
    }
}
Kotlin

@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:

Java

@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) {
        // ...
    }
}
Kotlin

@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:

Java

@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) {
        // ...
    }
}
Kotlin

@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 and POST.

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:

Java

@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...
    }
}
Kotlin

@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.

If you try to use 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:

Java

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);
Kotlin

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)
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION