美文网首页springboot demo实战项目
springboot Gateway 2.x 跨域出现“Mult

springboot Gateway 2.x 跨域出现“Mult

作者: 灰色调诺言 | 来源:发表于2019-11-11 10:20 被阅读0次

    版权声明:本文为CSDN博主「记录的习惯」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/xht555/article/details/89484091

    更多demo请关注

    springboot demo实战项目
    java 脑洞
    java 面试宝典
    开源工具

    简介

    部署好gateway后,页面向gateway发送跨域Post请求,返回200但是浏览器报错,提示

    Multiple CORS header ‘Access-Control-Allow-Origin’ not allowed
    

    最终导致跨域请求失败。该错误已经提示得很明白了,意思是不允许多个跨越请求头“Access-Control-Allow-Origin”,错误现象如下:


    image.png image.png

    错误原因分析

    image.png

    跟踪了大半天,最终定位到问题所在,算是Spring Cloud Gateway的一个Bug,出问题的过滤器是NettyRoutingFilter这个过滤器,该过滤器位于Gateway过滤器链条的倒数第二位,看看问题出在哪里:

    问题解决

    既然问题出在过滤器链条上,那么还是用Spring的方式,增加一个过滤器,插入到过滤器链条中。不过,新增的这个过滤器在整个链条上的位置有特殊要求。
    当请求经过NettyRoutingFilter处理后,并不会马上响应客户端请求,接下来还有重要的一步要做,那就是处理响应体(ResponseBoby),由NettyWriteResponseFilter这个过滤器来处理,所以,要修复这个问题,就在处理完响应体之后立马再处理重复的跨域请求头就OK了,具体代码如下:

    跨域请求头重复处理过滤器:CorsResponseHeaderFilter.java

    package com.einwin.platform.edge.filter;
    
    import java.util.ArrayList;
    
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.HttpHeaders;
    import org.springframework.web.server.ServerWebExchange;
    
    import reactor.core.publisher.Mono;
    
    /**
     * 跨域请求头处理过滤器扩展
     * @Author 
     * @Create 2019-04-22 14:20:06
     */
    public class CorsResponseHeaderFilter implements GlobalFilter, Ordered {
        @Override
        public int getOrder() {
            // 指定此过滤器位于NettyWriteResponseFilter之后
            // 即待处理完响应体后接着处理响应头
            return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER + 1;
        }
    
        @Override
        @SuppressWarnings("serial")
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return chain.filter(exchange).then(Mono.defer(() -> {
                exchange.getResponse().getHeaders().entrySet().stream()
                        .filter(kv -> (kv.getValue() != null && kv.getValue().size() > 1))
                        .filter(kv -> (kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN) 
                                || kv.getKey().equals(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)))
                        .forEach(kv -> 
                {
                    kv.setValue(new ArrayList<String>() {{add(kv.getValue().get(0));}});
                });
                
                return chain.filter(exchange);
            }));
        }
    }
    

    跨域全局配置:CorsConfiguration.java

    package com.einwin.platform.edge.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.HttpMethod;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.reactive.CorsWebFilter;
    import org.springframework.web.cors.reactive.DefaultCorsProcessor;
    import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.util.pattern.PathPatternParser;
    
    import com.einwin.platform.edge.filter.CorsResponseHeaderFilter;
    import com.einwin.platform.sso.common.RequestHeaderKeys;
    
    /**
     * 网关跨域配置
     * @Author 
     * @Create 2019-04-19 15:29:54
     */
    @Configuration
    public class CorsConfiguration {
        @Bean
        public CorsResponseHeaderFilter corsResponseHeaderFilter() {
            return new CorsResponseHeaderFilter();
        }
        
        @Bean
        public CorsWebFilter corsFilter() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
            source.registerCorsConfiguration("/**", buildCorsConfiguration());
            
            CorsWebFilter corsWebFilter = new CorsWebFilter(source, new DefaultCorsProcessor() {
                @Override
                protected boolean handleInternal(ServerWebExchange exchange, CorsConfiguration config, 
                    boolean preFlightRequest) 
                {
                    // 预留扩展点
                    // if (exchange.getRequest().getMethod() == HttpMethod.OPTIONS) {
                        return super.handleInternal(exchange, config, preFlightRequest);
                    // }
    
                    // return true;
                }
            });
            
            return corsWebFilter;
        }
        
        private CorsConfiguration buildCorsConfiguration() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.addAllowedOrigin("*");
            
            corsConfiguration.addAllowedMethod(HttpMethod.OPTIONS);
            corsConfiguration.addAllowedMethod(HttpMethod.POST);
            corsConfiguration.addAllowedMethod(HttpMethod.GET);
            corsConfiguration.addAllowedMethod(HttpMethod.PUT);
            corsConfiguration.addAllowedMethod(HttpMethod.DELETE);
            corsConfiguration.addAllowedMethod(HttpMethod.PATCH);
            // corsConfiguration.addAllowedMethod("*");
            
            corsConfiguration.addAllowedHeader("origin");
            corsConfiguration.addAllowedHeader("content-type");
            corsConfiguration.addAllowedHeader("accept");
            corsConfiguration.addAllowedHeader("x-requested-with");
            corsConfiguration.addAllowedHeader("Referer");
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.USER_AGENT);
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.TOKEN);
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.REFRESH_TOKEN);
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.OS);
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.X_APP_KEY);
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.X_DEVICE_ID);
            corsConfiguration.addAllowedHeader(RequestHeaderKeys.X_TOKEN);
            // corsConfiguration.addAllowedHeader("*");
            
            corsConfiguration.setMaxAge(7200L);
            corsConfiguration.setAllowCredentials(true);
            return corsConfiguration;
        }
    }
    

    OK,问题完美解决!

    相关文章

      网友评论

        本文标题:springboot Gateway 2.x 跨域出现“Mult

        本文链接:https://www.haomeiwen.com/subject/ksgzbctx.html