19

Why is my 'Access-Control-Allow-Credentials' no longer being sent in response to preflight calls (OPTIONS) under Spring Boot 2.0.x (2.0.1.RELEASE in my case)? Here is my Global CORS Configuration that works fine under Spring Boot 1.5.6:

@Configuration
public class CorsConfig {

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins(
                        "http://localhost:3000",..)
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD");
        }
    };
}}

My pom dependencies (I am doing my own security and avoiding Spring Security):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

My service call to the REST endpoints fails the preflight:

Failed to load http://localhost:8080/api/v5/sec/auth: Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin 'http://localhost:3000' is therefore not allowed access.

I have verified that 'Access-Control-Allow-Credentials' header is indeed present in the case of Spring Boot 1.5.6 and missing under Spring Boot 2.0.1.

All the documentation I can find, including the latest on spring.io here, says my global configuration is still correct, even though WebMvcConfigurerAdapter appears to be deprecated now.


UPDATE:


Here are the response headers before and after the migrate:

Before Migrate (Spring Boot 1.5.6):

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://localhost:3000
Content-Type: application/json;charset=UTF-8
Date: Day, dd Mon yyyy hh:mm:ss GMT
Transfer-Encoding: chunked
Vary: Origin

After Migrate (Spring Boot 2.0.1 - Access-Control-Allow-Credentials header missing, but others changed/added):

Access-Control-Allow-Headers: content-type
Access-Control-Allow-Methods: GET,HEAD,POST <-- My specified methods ignored
Access-Control-Allow-Origin: * <-- My specified origin ignored
Access-Control-Max-Age: 1800
Content-Length: 0
Date: Day, dd Mon yyyy hh:mm:ss GMT
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers

Juan Diego Lozano
  • 989
  • 2
  • 18
  • 30
Geyser14
  • 1,385
  • 3
  • 14
  • 32

5 Answers5

44

This was missing from the Spring doc and many examples but the answer was very easy. I just saw the allowCredentials() method on CorsRegistry and added .allowCredentials(true) to the registry method chain and that added the Access-Control-Allow-Credentials header back in.

Also, I no longer use the deprecated WebMvcConfigurerAdapter, but now implement WebMvcConfigurer and override the addCorsMappings() method.

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/**")
                .allowedOrigins(
                        "http://localhost:3000",..)
                .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD")
                .allowCredentials(true)
        ;
    }
    
}
Juan Diego Lozano
  • 989
  • 2
  • 18
  • 30
Geyser14
  • 1,385
  • 3
  • 14
  • 32
  • 1
    For my case, I changed `registry.addMapping("/**")` to `registry.addMapping("/" )` and it worked fine – Ghassen Jan 31 '19 at 16:50
  • 1
    This is a great answer, better than the official documentation of Spring. The spring documentation refers to usnig the WebMvcConfigurerAdapter, which is a bad idea because once you do, you'll be warned by your IDE that it is deprecated. – Marcus Ekström Aug 21 '19 at 04:53
  • Hi I'm also facing the same issue but above fix didn't worked for me. @sammoussa.ua Can you guys please let me know if anything else I need to look into. – Achu Feb 19 '20 at 16:43
  • You will need to define "didn't worked". What you tried, how it failed, code, etc. Consider posting a new question with those items. – Geyser14 Feb 20 '20 at 21:13
4

If you are using Spring Boot 2.0.x

CORS support is disabled by default and is only enabled once the management.endpoints.web.cors.allowed-origins property has been set. The following configuration permits GET and POST calls from the example.com domain:

management.endpoints.web.cors.allowed-origins=http://example.com management.endpoints.web.cors.allowed-methods=GET,POST

For more information refer

Abhijit
  • 374
  • 3
  • 15
4

I'm using spring boot 2.0.2. I have the same issue, but I use the following code to fix it. Does anybody have the best way?

//    Miss `Access-Control-Allow-Origin` header in response using this bean. 
//    @Bean
//    CorsConfigurationSource corsConfigurationSource() {
//        CorsConfiguration configuration = new CorsConfiguration();
//        configuration.setAllowCredentials(true);
//        configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
//        configuration.addAllowedMethod("*");
//        configuration.setAllowedOrigins(this.getAllowedOrigins());
//        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
//        source.registerCorsConfiguration("/**", configuration);
//        return source;
//    }

    @Bean
    public FilterRegistrationBean<CorsFilter> initCorsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
        config.addAllowedMethod("*");
        config.setAllowedOrigins(this.getAllowedOrigins());
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }
Bendy Zhang
  • 451
  • 2
  • 10
3

Step 1 : Spring already has a CorsFilter even though You can just register your own CorsFilter as a bean to provide your own cofiguration.

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Provide list of origins if you want multiple origins
    config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
    config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"));
    config.setAllowCredentials(true);
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}

Step 2 : Annotate the controller with @CrossOrigin annotation.

Saveendra Ekanayake
  • 3,153
  • 6
  • 34
  • 44
  • 1
    This one worked for me. Without messing with my code I simply added this bean and added @CrossOrigin on the controller to make it work. Thanks – Abhishek Aggarwal Apr 04 '20 at 05:50
0

This works for me (Kotlin):

@Configuration
class CorsConfig : WebMvcConfigurer {
    override fun addCorsMappings(registry: CorsRegistry) {
        registry.addMapping("/**")
    }
}
Dmitry Kaltovich
  • 2,046
  • 19
  • 21