35

I need to add CORS filter to my Spring Boot web application.

I have added CORS mappings as described in the following documentation http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html

This is my config:

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // @formatter:off   
        registry
            .addMapping("/**")
            .allowedOrigins(CrossOrigin.DEFAULT_ORIGINS)
            .allowedHeaders(CrossOrigin.DEFAULT_ALLOWED_HEADERS)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600L);
        // @formatter:on
    }

...

}

Right now when I'm trying to access my API I receiving a following error:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://example.com/api/v1.0/user. (Reason: CORS preflight channel did not succeed).

This is a screenshot from FF console:

enter image description here

What am I doing wrong and how to properly configure CORS headers in order to avoid this issue ?

sideshowbarker
  • 62,215
  • 21
  • 143
  • 153
alexanoid
  • 19,599
  • 35
  • 160
  • 321

12 Answers12

81

I have fixed this issue by creating a new CORS Filter:

@Component
public class CorsFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token");
        response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else { 
            filterChain.doFilter(request, response);
        }
    }
}

and added it to securty configuration:

.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class)

UPDATED - More modern way nowadays which I switched to:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

        http
            .cors()
        .and()

        ...
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
        configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

}
alexanoid
  • 19,599
  • 35
  • 160
  • 321
  • 1
    If you want to support proper RESTful APIs, shouldn't you add also the PUT verb in there? – Marco Tedone Apr 24 '16 at 11:03
  • 1
    I used the above example code and others too, but none of them worked on the application level. Eventually, I set the CORS over controller level. – Nadeem Jamali May 22 '18 at 10:39
  • Our project is not MVC and your not updated solution helped. – Davut Gürbüz Feb 11 '19 at 09:58
  • @alexanoid Does the last option using the CorsConfigurationSource allows to dynamically load the allowed domains from, for example, a database? Use case: an admin web app where you can control the allowed domains for your application. – Sebastiandg7 Apr 22 '19 at 14:48
  • 1
    This is the only configuration which can work with $http.get in AngularJs 1.6 and I found this after hours of trying with no understand about why – Bằng Rikimaru Aug 15 '19 at 17:12
14

Had the same issue getting CORS to work with spring data rest, this was the filter code I used.

    /**
 * Until url{https://jira.spring.io/browse/DATAREST-573} is fixed
 * 
 * @return
 */
@Bean
public CorsFilter corsFilter() {

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    //config.setAllowCredentials(true); // you USUALLY want this
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("HEAD");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("DELETE");
    config.addAllowedMethod("PATCH");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}
Romell
  • 457
  • 3
  • 12
  • 1
    It's best configuration, because it works not only for controllers but also for interceptors! Thx! – Maciek Kreft Jul 13 '18 at 21:59
  • You know, I like Spring, but CORS configuration has been causing me no end of grief. Everytime I upgrade my Springboot version, CORS is broken again for my REST app. My filter isn't being picked up. It isn't at the top. Doesn't work if you included spring-data. Always something. For the last round, this helped me for my oauth token retrieval, but I still had to keep a filter to handle the `OPTIONS` pre-flight: `if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); }` – Glenn Oct 11 '19 at 01:45
  • Sorry but where do you place the above code in? Or do I need to create a new class filter and create the above Bean inside the new class? – Henry Jan 02 '20 at 06:45
  • The code is placed in any class that has the `@Configuration` annotation but there are easier ways to do this with Spring Boot 2, see comments further down the page – Romell Jan 03 '20 at 12:43
7

This is very simple and working well. Within the class you wrote for Web Security Configurations, enter this line httpSecury.cors();


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {

         httpSecurity.cors();     //  This enables cors

        // Your codes

    }

}


4

I still had the CORS error after following the two tutorials:

First I followed the Web Security guide: https://spring.io/guides/gs/securing-web/#scratch

Second I followed the CORS guide: https://spring.io/guides/gs/rest-service-cors/#global-cors-configuration

To resolve my issues after following these guides I had to add http.cors() to the http security.

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

Adding the .cors() allows it to use the @Bean I declared for my CORS configuration.

@Bean
public WebMvcConfigurer corsConfigurer() {
  return new WebMvcConfigurer() {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
      registry.addMapping("/**").allowedOrigins("http://localhost:4200");
    }
  };
}
Community
  • 1
  • 1
Gabe Gates
  • 586
  • 5
  • 19
  • Documentations and forum posts everywhere was giving same procedure but none of them mentioned this requirement to call `http.cors()` inside the `void configure(HttpSecurity http)` implementation. I'm really thankful because this fixed my problem ultimately after a long struggle. Is this actually documented somewhere? – rineez Jun 25 '20 at 09:45
  • I had to do this inside my class with `@EnableWebSecurity` annotation, that extends WebSecurityConfigurerAdapter. (I guess it will may be a direct implementation of `WebSecurityConfigurer` interface for some others) – rineez Jun 25 '20 at 09:48
3

For what its worth, the following combination solution worked for me:

1.

@Configuration
public class CorsConfiguration {

//This can be used in combination with @CrossOrigin on the controller & method.

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedMethods("HEAD","OPTIONS")
                        .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept");
            }
        };
    }
}

2. @CrossOrigin on the RestController class. Having @CrossOrigin reads the @RequestMapping annotations and the HTTP methods in it. Rest of the requests are rejected with CORS error.

But you will be out of luck with the above solution if you want to use spring security in your project.

I am using spring boot version 1.5.4.RELEASE.

Vijay Kalidindi
  • 125
  • 2
  • 11
1

if using spring-boot 2 below code is enough to solve cors issue and preflight issue

@Override
    public void configure(WebSecurity web) throws Exception {
//      web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
        web.ignoring().antMatchers("/resources/**", "/index.html", "/login.html", "/partials/**", "/template/**", "/",
                "/error/**", "/h2-console", "*/h2-console/*");
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.applyPermitDefaultValues();
        config.setAllowCredentials(true);// this line is important it sends only specified domain instead of *
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
1

It's work for me

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS");
    }
}
hang gao
  • 221
  • 4
  • 5
1

Step 1: Add this annotation in your controller

@CrossOrigin
public class JobController {}

Step2 : Add this in any of your Configuration

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("*");
        }
    };
}

This will work only if you have @CrossOrigin annotation on your controller

1

If you are using CORS with Spring Security, this is the latest documentation: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors

This is similar to the code quoted elsewhere on this page:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors(withDefaults())
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

Although there are other places where you can configure CORS, it makes sense to do it as part of your security configuration because they are tightly related in that the CORS processing has to happen before the security processing - something that previous posts have noted. The reason given in the above quoted doc is that:

"CORS must be processed before Spring Security because the pre-flight request will not contain any cookies (i.e. the JSESSIONID). If the request does not contain any cookies and Spring Security is first, the request will determine the user is not authenticated (since there are no cookies in the request) and reject it."

Adding the .cors() line at the start of the http configuration - as shown above - makes that happen. Otherwise the pre flight OPTIONS request will go unresponded.

jadc
  • 53
  • 1
  • 7
0

Proper handling of the pre-flight OPTIONS request is necessary, but NOT SUFFICIENT for cross-site resource requests to work.

After the OPTIONS request comes back with satisfactory headers, all responses to any subsequent requests to the same URL also have to have the necessary "Access-Control-Allow-Origin" header, otherwise the browser will swallow them, and they won't even show up in the debugger window. https://stackoverflow.com/a/11951532/5649869

Community
  • 1
  • 1
  • At the beginning leave only required http methods in allowed methods. Then try make raw request using curl `curl -H "Origin: http://127.0.0.1" --verbose http://127.0.0.1/api/v1.0/user`. You have to see Access-Control-Allowed-* headers in response. And finally, try to specify you host (http://127.0.0.1 or smth. else) in AllowedOrigin. – Алексей Романов Apr 23 '16 at 23:24
  • I have tried `allowedHeaders("xsrf-token").exposedHeaders("xsrf-token")` but still doesn't working. I think I need something like this - `if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); }` but I don't know how to add this logic into `CorsRegistry registry` – alexanoid Apr 24 '16 at 07:27
0

The current recommended way of doing CORS is

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {

        registry.addMapping("/api/**")
            .allowedOrigins("http://domain2.com")
            .allowedMethods("PUT", "DELETE")
            .allowedHeaders("header1", "header2", "header3")
            .exposedHeaders("header1", "header2")
            .allowCredentials(true).maxAge(3600);

        // Add more mappings...
    }
}

This is base on https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors

But you also need to make sure that CORS is enabled and CSRF is disabled in your WebSecurityConfig file.

I once had an issue where all my POST methods are not working (returning 403 forbiden) while GET methods work just fine but this is solved after CSRF is disabled

Kolawole
  • 465
  • 5
  • 9
0

This was the piece of code that I used for Cors Configurations to work with Spring Boot. It's corsFilter configuration inside main application class.

Application is running on 'http://localhost:4200'

import org.springframework.context.annotation.Bean;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;


    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control-Allow-Origin", "Content-Type",
                "Accept", "Authorization", "Origin, Accept", "X-Requested-With",
                "Access-Control-Request-Method", "Access-Control-Request-Headers"));
        corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization",
                "Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials"));
        corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
        urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
        return new CorsFilter(urlBasedCorsConfigurationSource);
    }