alexanoid
alexanoid

Reputation: 25770

Spring Boot CORS filter - CORS preflight channel did not succeed

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 ?

Upvotes: 49

Views: 129528

Answers (13)

emilpmp
emilpmp

Reputation: 1736

AFAIK, for all the http requests, a preflight request is sent to the server in order to check the access of that particular api request. The preflight request is usually a "OPTION" http request which sends the metadata required for the coming request.

So the error, preflight channel didn't succeed means that the preflight request which was sent to the server got blocked or rejected. In most cases, this happens because

  • "OPTION" request is not in the allowed methods of spring security configuration
  • The origin of your UI is not allowed in spring security

As of Spring security 5.4.5 we can basically allow the above mentioned points to check whether this is the underlying problem.

Create or update the class which extends WebMvcConfigurer

@Override
public void addCorsMappings(CorsRegistry registry) {
    //The pattern, allowedOrigins and allowedMethods should be restricted to the frontend application url,
    //so that CORS attacks won't happen
    registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}

Here, addMapping takes a parameter of the "API endpoint", we are providing "*" to configure all the endpoints supported by the server.

allowedOrigins corresponds to the UI application paths supported for the mappings we provided above (*)

allowedMethods takes in an array of all the http methods allowed your server.

In a production environment this configuration we provide should be restricted to the appropriate values.

Also, in your configuration class which extends WebSecurityConfigurerAdapter

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

Note the 'http.cors()' methods we provided

Upvotes: 0

Vidumini Kulathunga
Vidumini Kulathunga

Reputation: 81

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


Upvotes: 3

jadc
jadc

Reputation: 362

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.

Upvotes: 4

meghana manjunath
meghana manjunath

Reputation: 49

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

Upvotes: 0

Gabe Gates
Gabe Gates

Reputation: 1000

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");
    }
  };
}

Upvotes: 5

Udith Indrakantha
Udith Indrakantha

Reputation: 970

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

    }

}


Upvotes: 7

hang gao
hang gao

Reputation: 461

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");
    }
}

Upvotes: 3

Niranjan Panigrahi
Niranjan Panigrahi

Reputation: 11

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;
    }

Upvotes: 1

Kolawole
Kolawole

Reputation: 525

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

Upvotes: 1

alexanoid
alexanoid

Reputation: 25770

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;
    }

}

Upvotes: 118

Vijay Kalidindi
Vijay Kalidindi

Reputation: 175

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.

Upvotes: 3

Romell
Romell

Reputation: 527

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

Upvotes: 21

Romanow Alexey
Romanow Alexey

Reputation: 198

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

Upvotes: 0

Related Questions