I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In a previous post, we learned about Cross-Origin Resource Sharing (CORS) specification and how to use it within Spring.

In this quick tutorial, we’ll set up a similar CORS configuration using Spring’s 5 WebFlux framework.

First of all, we’ll see how we can enable the mechanism on annotation-based APIs.

Then, we’ll analyze how to enable it on the whole project as a global configuration, or by using a special WebFilter.

2. Enabling CORS on Annotated Elements

Spring provides the @CrossOrigin annotation to enable CORS requests on controller classes and/or handler methods.

2.1. Using @CrossOrigin on a Request Handler Method

Let’s add this annotation to our mapped request method:

@CrossOrigin
@PutMapping("/cors-enabled-endpoint")
public Mono<String> corsEnabledEndpoint() {
    // ...
}

We’ll use a WebTestClient (as we explained in section ‘4. Testing’ of this post) to analyze the response we get from this endpoint :

ResponseSpec response = webTestClient.put()
  .uri("/cors-enabled-endpoint")
  .header("Origin", "http://any-origin.com")
  .exchange();

response.expectHeader()
  .valueEquals("Access-Control-Allow-Origin", "*");

In addition, we can try out a preflight request to make sure the CORS configuration is working as expected:

ResponseSpec response = webTestClient.options()
  .uri("/cors-enabled-endpoint")
  .header("Origin", "http://any-origin.com")
  .header("Access-Control-Request-Method", "PUT")
  .exchange();

response.expectHeader()
  .valueEquals("Access-Control-Allow-Origin", "*");
response.expectHeader()
  .valueEquals("Access-Control-Allow-Methods", "PUT");
response.expectHeader()
  .exists("Access-Control-Max-Age");

The @CrossOrigin annotation has the following default configuration:

  • Allows all origins (that explains the ‘*’ value in the response header)
  • Allows all headers
  • All HTTP methods mapped by the handler method are allowed
  • Credentials are not enabled
  • The ‘max-age’ value is of 1800 seconds (30 minutes)

However, any of these values can be overridden using the annotation’s parameters.

2.2. Using @CrossOrigin on the Controller

This annotation is also supported at a class level, and it will affect all its methods.

In case the class-level configuration isn’t suitable for all our methods, we can annotate both elements to get the desired result:

@CrossOrigin(value = { "http://allowed-origin.com" },
  allowedHeaders = { "Baeldung-Allowed" },
  maxAge = 900
)
@RestController
public class CorsOnClassController {

    @PutMapping("/cors-enabled-endpoint")
    public Mono<String> corsEnabledEndpoint() {
        // ...
    }

    @CrossOrigin({ "http://another-allowed-origin.com" })
    @PutMapping("/endpoint-with-extra-origin-allowed")
    public Mono<String> corsEnabledWithExtraAllowedOrigin() {
        // ...
    }

    // ...
}

3. Enabling CORS on the Global Configuration

We can also define a global CORS configuration by overriding the addCorsMappings() method of a WebFluxConfigurer implementation.

In addition, the implementation needs the @EnableWebFlux annotation to import the Spring WebFlux configuration:

@Configuration
@EnableWebFlux
public class CorsGlobalConfiguration implements WebFluxConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry corsRegistry) {
        corsRegistry.addMapping("/**")
          .allowedOrigins("http://allowed-origin.com")
          .allowedMethods("PUT")
          .maxAge(3600);
    }
}

As a result, we are enabling cross-origin request handling for that particular path pattern.

The default configuration is similar to the @CrossOrigin one, but with only the GET, HEAD, and POST methods allowed.

We can also combine this configuration with a local one:

  • For the multiple-value attributes, the resulting CORS configuration will be the addition of each specification
  • On the other hand, the local values will have precedence over the global ones for the single-value ones

Using this approach is not effective for functional endpoints, though.

4. Enabling CORS with a WebFilter

The best way to enable CORS on functional endpoints is by using a WebFilter.

As we’ve seen in this post, we can use WebFilters to modify requests and responses, while keeping the endpoint’s implementation intact.

Spring provides the built-in CorsWebFilter so as to deal with the cross-origin configurations easily:

@Bean
CorsWebFilter corsWebFilter() {
    CorsConfiguration corsConfig = new CorsConfiguration();
    corsConfig.setAllowedOrigins(Arrays.asList("http://allowed-origin.com"));
    corsConfig.setMaxAge(8000L);
    corsConfig.addAllowedMethod("PUT");
    corsConfig.addAllowedHeader("Baeldung-Allowed");

    UrlBasedCorsConfigurationSource source =
      new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", corsConfig);

    return new CorsWebFilter(source);
}

This is also effective for annotated handlers, but it can’t be combined with a more fine-grained @CrossOrigin configuration.

We have to keep in mind that the CorsConfiguration doesn’t have a default configuration.

Thus, unless we specify all the relevant attributes, the CORS implementation will be pretty much restrictive.

A simple way of setting the default values is by using the applyPermitDefaultValues() method on the object.

5. Conclusion

In conclusion, we learned with very short examples of how to enable CORS on our webflux-based service.

We saw different approaches, therefore all we have to do now is analyze which one suits our requirements best.

We can find plenty of examples in our Github repo, together with test cases where we analyze most of the edge cases regarding this topic.

I just announced the new Spring Boot 2 material, coming in REST With Spring:

>> CHECK OUT THE LESSONS