版權(quán)聲明:本文為CSDN博主「記錄的習(xí)慣」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/xht555/article/details/89484091
更多demo請關(guān)注
springboot demo實戰(zhàn)項目
java 腦洞
java 面試寶典
開源工具
簡介
部署好gateway后,頁面向gateway發(fā)送跨域Post請求,返回200但是瀏覽器報錯,提示
Multiple CORS header ‘Access-Control-Allow-Origin’ not allowed
最終導(dǎo)致跨域請求失敗。該錯誤已經(jīng)提示得很明白了,意思是不允許多個跨越請求頭“Access-Control-Allow-Origin”,錯誤現(xiàn)象如下:
錯誤原因分析
跟蹤了大半天,最終定位到問題所在,算是Spring Cloud Gateway的一個Bug,出問題的過濾器是NettyRoutingFilter這個過濾器,該過濾器位于Gateway過濾器鏈條的倒數(shù)第二位,看看問題出在哪里:
問題解決
既然問題出在過濾器鏈條上,那么還是用Spring的方式,增加一個過濾器,插入到過濾器鏈條中。不過,新增的這個過濾器在整個鏈條上的位置有特殊要求。
當(dāng)請求經(jīng)過NettyRoutingFilter處理后,并不會馬上響應(yīng)客戶端請求,接下來還有重要的一步要做,那就是處理響應(yīng)體(ResponseBoby),由NettyWriteResponseFilter這個過濾器來處理,所以,要修復(fù)這個問題,就在處理完響應(yīng)體之后立馬再處理重復(fù)的跨域請求頭就OK了,具體代碼如下:
跨域請求頭重復(fù)處理過濾器: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之后
// 即待處理完響應(yīng)體后接著處理響應(yīng)頭
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;
/**
* 網(wǎng)關(guān)跨域配置
* @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)
{
// 預(yù)留擴展點
// 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,問題完美解決!