Spring Global CORS configuration not working but Controller level config does
Asked Answered
A

11

44

I am trying to configure CORS globally via WebMvcConfigurerAdapter shown below. To test I am hitting my API endpoint via a small node app I created to emulate an external service. When I try this approach the response does not contain the correct headers and fails with

XMLHttpRequest cannot load http://localhost:8080/api/query/1121. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:333' is therefore not allowed access.

Global Config

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@EnableWebMvc
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/api/query/**")
                    .allowedOrigins("*")
                    .allowedHeaders("*")
                    .allowCredentials(true);
        }
}

However when I utilize the @CrossOrigin annotation like so it works just fine responding with the proper headers.

@CrossOrigin(origins = "*", allowCredentials = "true", allowedHeaders = "*")
@RestController
@RequestMapping(value = "/api/query", produces = MediaType.APPLICATION_JSON_VALUE)
public class QueryController {
   ......
}

Produces

Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost:333

What am I missing to make the global config work (followed instructions here https://spring.io/blog/2015/06/08/cors-support-in-spring-framework). I feel like I'm missing something simple since annotating the controller works just fine.

Afterwards answered 23/6, 2016 at 1:38 Comment(3)
Maybe .allowedOrigins("*").allowedHeaders("*") are redundant in global configurationVietnam
Did you figure it out? I am also having this issue. Tried answers below but didnt work for me...Flugelhorn
@Flugelhorn I was in the same boat as you, but managed to get something working. Have a look at my answer on here: https://mcmap.net/q/377320/-spring-global-cors-configuration-not-working-but-controller-level-config-doesKutz
L
25

In order for the global CORS config to work, the client must add these two headers in the OPTIONS request.

Origin: http://host.com
Access-Control-Request-Method: POST

However the @CrossOrigin annotation requires just the "Origin" header.
Your client probably adds the "Origin" header but is missing the "Access-Control-Request-Method".....thats why it works for you with the @CrossOrigin, but doesn't with the global config.

Lexicologist answered 3/7, 2016 at 10:25 Comment(2)
I'm getting Refused to set unsafe header "Access-Control-Request-Method", chrome ` 83.0.4103.61 (Official Build) (64-bit)`Dictograph
The client can't do this if it's code running in a web browser.Dehypnotize
N
11

you didn't declared method in it which is by default accept only get method. try registry.allowedMethods("*");

Nuke answered 2/7, 2016 at 13:38 Comment(1)
I am facing the same issue with POST method. it solved by adding allowedmethods.Germinant
J
4

I was facing the same issue and after setting the maxAge attribute everything started working ok!

@Bean
public WebMvcConfigurer CORSConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedHeaders("*")
                    .allowedMethods("GET", "POST", "PUT", "DELETE", "HEAD")
                    .maxAge(-1)   // add maxAge
                    .allowCredentials(false);
        }
    };
}

if you check the CrossOrigin annotation it has a default value assigned to that attribute

/**
 * <p>By default this is set to {@code 1800} seconds (30 minutes).
 */
long maxAge() default -1;
Jealous answered 13/7, 2019 at 17:17 Comment(0)
S
3

I was able to get the Spring Global CORS configuration to work, after experiencing the exact problem documented in this issue. I had to do 2 things:

  1. allowedOrigins cannot be * if allowCredentials is true. This is documented at Mozilla.org

  2. Remove mvc:annotation-driven from the spring XML. Cannot have BOTH the XML config and @EnableWebMvc. The global class won't work without @EnableWebMvc, thus mvc:annotation-driven must be removed.

Separates answered 21/12, 2017 at 3:3 Comment(0)
F
3

I had a similar issue and none of methods seemed to work (except using @CrossOrigin annotation for each controller). I followed Bharat Singh's solution above and after some debugging of Spring Framework internals - here's what worked for me (Spring Boot 2.0.6 + Spring Framework 5.0.10):

@Configuration
public class WebMvcConfiguration extends WebMvcConfigurationSupport {

/* (non-Javadoc)
 * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry)
 */
@Override
protected void addCorsMappings(CorsRegistry registry) {
    //NOTE: servlet context set in "application.properties" is "/api" and request like "/api/session/login" resolves here to "/session/login"!
    registry.addMapping("/**")
        .allowedMethods("GET", "POST", "PUT", "DELETE")
        .allowedOrigins("*")
        .allowedHeaders("*")
        .allowCredentials(false);
    }
}

Initially when I used "/api/**" mapping it was configured within Spring, but since the application was deployed with "/api" context - requests like "/api/session/login" were internally mapped to "/session/login" and such mapping in CORS configuration was not found - please pay attention to that!

Fulmar answered 31/10, 2018 at 9:17 Comment(1)
Thank you so much. I was banging my head on this one for ages. I use server.servlet.context-path to set my API prefix to /api and that was totally breaking the cors mapping! So frustrating.Towandatoward
K
3

I've just been having the exact same issue, with none of the solutions in this thread working. I've managed to solve it with the following:

A new configuration bean:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsFilterConfiguration {

  @Bean
  public CorsFilter corsFilter() {
      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
      CorsConfiguration config = new CorsConfiguration();
      config.setAllowCredentials(true);
      config.addAllowedOrigin("*");
      config.setAllowedMethods(Arrays.asList("POST", "OPTIONS", "GET", "DELETE", "PUT"));
      config.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization"));
      source.registerCorsConfiguration("/**", config);
      return new CorsFilter(source);
  }
}

A modification to my web.xml to add a new filter for all URLs:

<filter>
  <filter-name>corsFilter</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>corsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Obviously you can modify the cors config accordingly.

Kutz answered 11/4, 2019 at 9:51 Comment(0)
B
2

I faced similar issue. I changed the WebMvcConfigurerAdapter to WebMvcConfigurationSupport and it started working.

In addition to this I also moved the RequestMappingHandlerMapping defined in xml configuration file to java configuration.

Brynn answered 31/1, 2017 at 12:6 Comment(0)
C
2

I have had the same problem working on a Spring mvc application (not Spring Boot). The only way I could solve the problem was to explicitly add a cors filter to the spring security filter chain:

public class MySecurityInitializer extends AbstractSecurityWebApplicationInitializer {

  protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
    final FilterRegistration.Dynamic corsFilter = servletContext.addFilter("corsFilter", corsFilter());
    corsFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
  }

  protected CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowedOrigins(Arrays.asList(CrossOrigin.DEFAULT_ORIGINS));
    config.setAllowedMethods(Arrays.asList("POST", "OPTIONS", "GET", "DELETE", "PUT"));
    config.setAllowedHeaders(Arrays.asList(CrossOrigin.DEFAULT_ALLOWED_HEADERS));
    config.setAllowCredentials(CrossOrigin.DEFAULT_ALLOW_CREDENTIALS);
    config.setMaxAge(CrossOrigin.DEFAULT_MAX_AGE);
    source.registerCorsConfiguration("/**", config);

    return new CorsFilter(source);
  }

}

Luck!

Coppage answered 24/3, 2020 at 0:10 Comment(0)
L
1

I was facing similar issue like this, every time it shows Cors policy error while i am trying to access an endpoint

that was due to the change in SecurityConfig class i have added Authority role as "ADMIN" and everytime adding this frontend not able to access the endpoint

So i added cors(Customizer.withDefaults())

   @Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .cors(Customizer.withDefaults()) 
            .authorizeRequests()
            .antMatchers("**/user/register", "/login", "/image").permitAll() // Allow access to public endpoints
            .antMatchers("/user/view-all-user").hasAnyRole("ADMIN") // Require authentication for protected endpoint
            .anyRequest().permitAll() // Allow access to all other endpoints
            .and()
            .formLogin().loginPage("/login").permitAll();
}

this fixed my issue with the CORS policy.

Langland answered 27/2 at 5:44 Comment(0)
C
0

I think your mapping definition is missing a *:

registry.addMapping("/api/query/**")

Without that extra *, this configuration is not mapped to the /api/query/1121 request path (but it would work on /api/query/5).

Caddaric answered 23/6, 2016 at 8:31 Comment(1)
Thank you for your response, I also tried "/api/query/**" and it didn't work. Originally I even had a 'hello world' type implementation with registry.addMapping("/**") and that didn't make a difference.. do I have to somehow specify that it is on port 8080? I don't even know ... it looks like it should work fineAfterwards
M
0

I was trying to configure CORS globally via WebMvcConfigurerAdapter shown yours with log. And I found log message but it got error like

Access to XMLHttpRequest at 'myurl' from origin 'some origin' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Controll-Allow-Origin' header is presnet on the requested resource.

My Case was also good at @CrossOrigin but not in globally configure. I hope this will be helped.

The reason is web.xml.

1. I have <context:component-scan base-package="some base-package"> but class WebConfig is not in 'some base-package' package, It's in the upper package. I moved WebConfig to 'some base-package' package and

2. I removed <mvc:annotation-driven /> because I have already have @EnableWebMvc in WebConfig class.

It works.

Methanol answered 9/12, 2020 at 8:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.