spring cloud gateway Cross domain Global CORS Configuration Mode

  • 2021-10-27 07:34:08
  • OfStack

In Spring 5 Webflux, configure CORS by customizing WebFilter:

Note: This writing requires real cross-domain access, and the corresponding attributes will only be brought in the monitoring header.

Code implementation mode


import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.http.HttpMethod;
import reactor.core.publisher.Mono;
 
import static org.springframework.web.cors.CorsConfiguration.ALL;
public class XXXApplication{
public static void main(String[] args) {
    SpringApplication.run(XXXApplication.class, args);
}
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
    return (ServerWebExchange ctx, WebFilterChain chain) -> {
        ServerHttpRequest request = ctx.getRequest();
        if (!CorsUtils.isCorsRequest(request)) {
            return chain.filter(ctx);
        }
        HttpHeaders requestHeaders = request.getHeaders();
        ServerHttpResponse response = ctx.getResponse();
        HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
        HttpHeaders headers = response.getHeaders();
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
        headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
        if (requestMethod != null) {
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
        }
        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
        headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
        if (request.getMethod() == HttpMethod.OPTIONS) {
            response.setStatusCode(HttpStatus.OK);
            return Mono.empty();
        }
        return chain.filter(ctx);
    };
}
}

Configuration implementation mode

There is also a configuration writing method mentioned on the Internet, which is easy to use in actual measurement:


spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "*"

Cross-domain solution of springcloud gateway

There is something wrong with the cross-domain filter provided by springcloud gateway, and the front end will still report cross-domain. zuul won't have this problem. Debugging found that the main reason is that when the sightseeing device sends the sniffing request (OPTIONS), it does not return the cross-domain response header, so the sightseeing device reports the cross-domain problem.

Validation

Because springcloud gateway is the same service as webflux and zuul, zuul can pass and gateway reports an error by adopting the built-in cross-domain filter of spring. The specific configuration is as follows:

1. gateway cross-domain configuration

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            #  Allow to carry authentication information 
            #  Allow cross-domain sources ( Website domain name /ip) , setting * For all 
            #  Allow the in the cross-domain request head Field, setting * For all 
            #  Allow cross-domain method ,   Default to GET And OPTIONS , setting * For all 
            #  Validity period of cross-domain permission 
            allow-credentials: true
            allowed-origins: '*'
            allowed-headers: Content-Type,Content-Length, Authorization, Accept,X-Requested-With
            allowed-methods: '*'
            exposed-headers: Content-Type,Content-Length, Authorization, Accept,X-Requested-With
            max-age: 3600

This configuration is invalid, and the front end will still report cross-domain problems, mainly because the front end did not return cross-domain information when sending OPTIONS request

2. zuul gateway or other micro-service servlet

Injecting cross-domain filters into containers


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

/**
 * @author ZhouChuGang
 * @version 1.0
 * @project langangkj-commonm
 * @date 2020/5/4 12:24
 * @Description  Cross-domain filter configuration 
 */
@Slf4j
@configuration
@ConditionalOnMissingBean(CorsFilter.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class CorsFilterConfiguration {

    public CorsFilterConfiguration() {
        log.info("========== Injection cross-domain filter =============");
    }

    @Bean("corsFilter")
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        // # That allows requests to be submitted to this server URI , * Indicates that all are allowed 
        config.addAllowedOrigin(CorsConfiguration.ALL);
        //  Allow cookies Cross-domain 
        config.setAllowCredentials(true);
        // # Header information allowed to access ,* Indicates all 
        config.addAllowedHeader(CorsConfiguration.ALL);
        //  The method that allows the request to be submitted, * Indicates that all are allowed 
        config.addAllowedMethod(CorsConfiguration.ALL);
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }

    @Autowired
    @Qualifier("corsFilter")
    private CorsFilter corsFilter;

    /**
     *  Configure cross-domain filters 
     */
    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilterRegistration() {
        FilterRegistrationBean<CorsFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(corsFilter);
        registration.addUrlPatterns("/*");
        registration.setName("corsFilter");
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registration;
    }
}

This scheme can perfectly solve cross-domain problems. But springcloud gateway is not the servlet specification.

Solutions

1. Cross-domain implementation of microservices behind gateway

Cross-domain is implemented by the service behind the gateway.

2. Implement a filter for cross-domain permission

You need to add the following information to the response header


#  This is in the request header  origin
add_header 'Access-Control-Allow-Origin' '$http_origin' ;
add_header 'Access-Control-Allow-Credentials' 'true' ;
add_header 'Access-Control-Allow-Methods' 'PUT,POST,GET,DELETE,OPTIONS' ;
add_header 'Access-Control-Allow-Headers' 'Content-Type,Content-Length,Authorization,Accept,X-Requested-With' ;
3. nginx is used as proxy to configure cross-domain response header. (Highly recommended)

The request goes to nginx first, then nginx goes to request gateway, and nginx adds cross-domain response header


add_header 'Access-Control-Allow-Origin' '$http_origin' ;
add_header 'Access-Control-Allow-Credentials' 'true' ;
add_header 'Access-Control-Allow-Methods' 'PUT,POST,GET,DELETE,OPTIONS' ;
add_header 'Access-Control-Allow-Headers' 'Content-Type,Content-Length,Authorization,Accept,X-Requested-With' ;

Here, for convenience, I use the third scheme to test the perfect solution!


Related articles: