springcloud gateway解决跨域问题

/**
 * 跨域允许
 */
@Configuration
public class CorsConfig {
 
    @Bean
    public WebFilter corsFilter() {
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest request = ctx.getRequest();
            if (CorsUtils.isCorsRequest(request)) {
                ServerHttpResponse response = ctx.getResponse();
                HttpHeaders headers = response.getHeaders();
                headers.set(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "*");
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "");
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600");
                if (request.getMethod() == HttpMethod.OPTIONS) {
                    response.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
            }
            return chain.filter(ctx);
        };
    }
}

参考博客:https://blog.csdn.net/a294634473/article/details/90715903

以上只是简单的处理,如果再网关和后端都进行了跨域配置,在返回的请求头中可能包含多个跨域的信息,那样同样会让请求出现异常,所以在上面的处理后,要将重复的跨域头信息给删除掉

在高一点的springcloud gateway中包含了DedupeResponseHeaderGatewayFilterFactory,可以进行过滤,如果低版本,没有这个类,自行从高版本中拷贝过来作为一个实现类,然后添加上配置

- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin Access-Control-Allow-Methods Access-Control-Allow-Headers Vary

附件代码:

GwCorsFilter.java

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.util.pattern.PathPatternParser;

@Configuration
public class GwCorsFilter {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();

        config.setAllowCredentials(true); // 允许cookies跨域
        config.addAllowedOrigin("*");// #允许向该服务器提交请求的URI,*表示全部允许,在SpringMVC中,如果设成*,会自动转成当前请求头中的Origin
        config.addAllowedHeader("*");// #允许访问的头信息,*表示全部
        config.addAllowedMethod("*");// 允许提交请求的方法类型,*表示全部允许
        config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了

        org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource source =
                new org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

 

DedupeResponseHeaderGatewayFilterFactory.java

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;

import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Mono;

/*
Use case: Both your legacy backend and your API gateway add CORS header values. So, your consumer ends up with
          Access-Control-Allow-Credentials: true, true
          Access-Control-Allow-Origin: https://musk.mars, https://musk.mars
(The one from the gateway will be the first of the two.) To fix, add
          DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

Configuration parameters:
- name
    String representing response header names, space separated. Required.
- strategy
    RETAIN_FIRST - Default. Retain the first value only.
    RETAIN_LAST - Retain the last value only.
    RETAIN_UNIQUE - Retain all unique values in the order of their first encounter.

Example 1
      default-filters:
      - DedupeResponseHeader=Access-Control-Allow-Credentials

Response header Access-Control-Allow-Credentials: true, false
Modified response header Access-Control-Allow-Credentials: true

Example 2
      default-filters:
      - DedupeResponseHeader=Access-Control-Allow-Credentials, RETAIN_LAST

Response header Access-Control-Allow-Credentials: true, false
Modified response header Access-Control-Allow-Credentials: false

Example 3
      default-filters:
      - DedupeResponseHeader=Access-Control-Allow-Credentials, RETAIN_UNIQUE

Response header Access-Control-Allow-Credentials: true, true
Modified response header Access-Control-Allow-Credentials: true
 */

/**
 * @author Vitaliy Pavlyuk
 */
@Slf4j
public class DedupeResponseHeaderGatewayFilterFactory extends
        AbstractGatewayFilterFactory<DedupeResponseHeaderGatewayFilterFactory.Config> {

    private static final String STRATEGY_KEY = "strategy";

    public DedupeResponseHeaderGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(NAME_KEY, STRATEGY_KEY);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange,
                    GatewayFilterChain chain) {
                return chain.filter(exchange).then(Mono.fromRunnable(
                        () -> dedupe(exchange.getResponse().getHeaders(), config)));
            }
        };
    }

    public enum Strategy {

        /**
         * Default: Retain the first value only.
         */
        RETAIN_FIRST,

        /**
         * Retain the last value only.
         */
        RETAIN_LAST,

        /**
         * Retain all unique values in the order of their first encounter.
         */
        RETAIN_UNIQUE

    }

    void dedupe(HttpHeaders headers, Config config) {
        String names = config.getName();
        Strategy strategy = config.getStrategy();
        if (headers == null || names == null || strategy == null) {
            return;
        }
        for (String name : names.split(" ")) {
            dedupe(headers, name.trim(), strategy);
        }
    }

    private void dedupe(HttpHeaders headers, String name, Strategy strategy) {
        List<String> values = headers.get(name);
        log.info("{}={}",name,values);
        if (values == null || values.size() <= 1) {
            return;
        }
        switch (strategy) {
        case RETAIN_FIRST:
            headers.set(name, values.get(0));
            break;
        case RETAIN_LAST:
            headers.set(name, values.get(values.size() - 1));
            break;
        case RETAIN_UNIQUE:
            headers.put(name, values.stream().distinct().collect(Collectors.toList()));
            break;
        default:
            break;
        }
    }

    public static class Config extends AbstractGatewayFilterFactory.NameConfig {

        private Strategy strategy = Strategy.RETAIN_FIRST;

        public Strategy getStrategy() {
            return strategy;
        }

        public Config setStrategy(Strategy strategy) {
            this.strategy = strategy;
            return this;
        }

    }

}

 

posted @ 2020-01-16 11:38  理舞  阅读(6318)  评论(0编辑  收藏  举报