CORS with Spring Boot

Spring Boot (2.1) : very basic configuration

With older spring security versions, it is needed to create our own CorsFilter class and to perform the whole CORS logic in, then to add it in the spring security filter chain.
Since spring security 4.2, things are a little simpler and overall we have multiple alternatives.
In any way, we enable CORS declaratively from the WebSecurityConfigurerAdapter.configure(HttpSecurity http) method as simply as :

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.cors()...
  }

The cors() javadoc states that :
Adds a CorsFilter to be used. If a bean by the name of corsFilter is provided, that CorsFilter is used. Else if corsConfigurationSource is defined, then that CorsConfiguration is used.

It means we have three options :
– to do no additional thing. In this case, Spring security will use the default class provided by Spring security and the default CorsConfigurationSource bean associated to.
– to declare a bean named corsFilter. In this case, Spring security will use it instead of the default.
– to declare a bean named corsConfigurationSource. In that case, Spring will use the default CorsFilter with the custom corsConfigurationSource declared by the application.
Here is a class that uses the second way :

 

@Configuration
public class CorsConfig {
 
  @Bean
  public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration corsConfiguration = new CorsConfiguration();
 
    corsConfiguration.setAllowCredentials(true);
    corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
    corsConfiguration.setAllowedMethods(Arrays.asList(CorsConfiguration.ALL));
    corsConfiguration.setAllowedHeaders(Arrays.asList(CorsConfiguration.ALL));
    source.registerCorsConfiguration("/**", corsConfiguration);
    return new CorsFilter(source);
  }
}

Using the second or the third way makes sense if we want to define specific values instead of default values provided by spring security corsFilter/corsConfiguration.

Spring Boot (2.1) : more complex configuration for more complex cases

The previous point adds the CORS feature for a specific security filter chain, that is this related to the chain context where cors() is invoked.
When we enable CORS in a chain/WebSecurityConfigurerAdapter that we declare, things are very simple and that way is nice.
In some cases, we need to enable CORS for requests matching to a provided/built-in filter chain but we don’t want to override the whole chain logic, we just want that it enable CORS for accepting pre-flight requests and « special » requests from other origins. That is the case when we use OAuth2 with Spring Boot for example.<br>
When overriding a built-in chain on a particular area is not straight, a simpler approach is adding CORS as a default/original filter.
In this way, CORS will work because any security filter chain first iterates on the original filters (the default), only then it iterates on the additional filters, that is filters configured specifically for a chain.
Here is how to declare that :

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
 
import java.util.Arrays;
 
@Configuration
public class CorsConfig {
 
  @Bean
  public FilterRegistrationBean customCorsFilter() {
    CorsConfiguration corsConfiguration = new CorsConfiguration();
 
    corsConfiguration.setAllowCredentials(true);
    corsConfiguration
        .setAllowedOrigins(Arrays.asList("http://localhost:4200", "http://localhost:89", "http://localhost:8282"));
    corsConfiguration.setAllowedMethods(Arrays.asList(CorsConfiguration.ALL));
    corsConfiguration.setAllowedHeaders(Arrays.asList(CorsConfiguration.ALL));
 
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", corsConfiguration);
 
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setName("COCO");
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
  }
}

We can see at runtime that the FilterChainProxy is composed of two types of filters : these that come from the originalChain, that contains default filters (not related to the security concern) and these specific to the security concern : the additional filters.

filterchain-original-and-additional-filters

Indeed, that solution works but I don’t like that because it mixes things that should not be mixed.
General http filters of Spring Web stack handles general concerns : encoding, traces, or for more complex cases a filter that delegate to a chain that handles filters for a specific requirement such as springSecurityFilterChain. Pulling out the security concern of CORS from the security chain to add it in the default filters is probably not the best thing to do. For example, traces related to security may become not understandable any longer, we don’t need either to invoke cors() any longer on the HttpSecurity object since here we don’t rely on the spring security built-in feature but on a spring web feature.
Later, I would like to show an alternative to that mixed way.

Client side (browser app)

Remember that CORS is to cope with browser security : respecting the same origin policy for « risky » requests.
It means that from the client side (angular, react or any js way) you don’t need to do anything : 
just send a request from an origin to another origin.
For example : request from the http://localhost:4200 origin to the http://localhost:8200 target should enable the CORS feature/protection of the browser.

Ce contenu a été publié dans Non classé. Vous pouvez le mettre en favoris avec ce permalien.

3 réponses à CORS with Spring Boot

  1. mahieddine dellabani dit :

    Really helpful post!
    I would like to know what’s the alternative that you are talking about when using the filter?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *