Spring Cloud Gateway Global Cors

·

0 min read

spring-framework-logo-png-6.png

Recently I am working on a new project using spring cloud to build a microservices architecture application. Because the frontend (vue.js app) needs to talk to the backend rest services, so we need to enable cors headers at the api gateway. The official documentation says:

You can configure the gateway to control CORS behavior. The “global” CORS configuration is a map of URL patterns to Spring Framework CorsConfiguration.

If we look at CorsConfiguration's javadoc , it is a container for CORS configuration. Because the CorsConfiguration settings in global CORS configuration is in plural, I think (it means I do not try yet) we can have multiple CorsConfigurations in global CORS configuration, each one has its own URL. In that javadoc, it teaches us a convenient way to use it, and I've seen some examples on the internet in this way:

By default a newly created CorsConfiguration does not permit any cross-origin requests and must be configured explicitly to indicate what should be allowed. Use applyPermitDefaultValues() to flip the initialization model to start with open defaults that permit all cross-origin requests for GET, HEAD, and POST requests.

Back to spring cloud gateway's documentation, it use yaml as its sample, but I prefer the traditional java property file. In the appendix , I find the property name in snake case:

spring.cloud.gateway.globalcors.add-to-simple-url-handler-mapping spring.cloud.gateway.globalcors.cors-configurations

After some experiments, it proves the camel case is also working. The final version of my global cors configurations are as following:

spring.cloud.gateway.discovery.locator.enabled= true spring.cloud.gateway.globalcors.add_to_simple_url_handler_mapping=true spring.cloud.gateway.globalcors.cors_configurations.[/].allowed_origins=* spring.cloud.gateway.globalcors.cors_configurations.[/].allowed_methods=* spring.cloud.gateway.globalcors.cors_configurations.[/].allowed_headers=* spring.cloud.gateway.globalcors.cors_configurations.[/].allow_credentials=true

And the camel case version:

spring.cloud.gateway.globalcors.addToSimpleUrlHandlerMapping=true spring.cloud.gateway.globalcors.corsConfigurations.[/].allowedOrigins=* spring.cloud.gateway.globalcors.corsConfigurations.[/].allowedMethods=* spring.cloud.gateway.globalcors.corsConfigurations.[/].allowedHeaders=* spring.cloud.gateway.globalcors.corsConfigurations.[/].allowCredentials=true

Add these global cors configurations in config server (Spring Cloud Config), we don't need to write any cors filter in api gateway or other microservices. Of course, if you don't like to put cors configurations in config server, you can provide a cors filter defined in a spring java config file like:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * Spring config class for cors filter.
 * see https://zhuanlan.zhihu.com/p/78978150
 */
@Configuration
public class CorsConfig {
  /**
   * 提供cors功能的filter.
   * @return a CorsWebFilter instance
   */
  @Bean
  public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedMethod("*");
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(
            new PathPatternParser());
    source.registerCorsConfiguration("/**", config);

    return new CorsWebFilter(source);
  }
}

P.S. When I update something, I run into a cors problem: preflight request failed. In the beginning, I blame it to spring cloud gateway, but finally I figure out that is caused by the api call made in the frontend: a parameter is missing. You can see in the following picture, the request url is http://localhost:10000/100/users/[object%20Object]. The "object" part should be an integer, because that integer is missing, a javascript object is used as that parameter and causes the preflight request fails.

screenshot 2020-05-04 11.15.50.jpg