Spring Cloud Gateway 2.x 跨域出现The 'Access-Control-Allow-Origin' header contains multiple values,....but only one is allowed 的问题解决
问题描述:vue前端调用后端登陆接口报错如下
Access to XMLHttpRequest at 'http://localhost:88/api/sys/login' from origin 'http://localhost:8002' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:8002, http://localhost:8002', but only one is allowed.
原因:spring NettyRoutingFilter 添加了两次Access-Control-Allow-Origin
解决方案:
1.新增CorsResponseHeaderFilter.java
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;/** * 跨域请求头处理过滤器扩展 */ 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); })); } }
2.跨域全局配置:CorsConfiguration.java
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; /** * 网关跨域配置 */ @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("*"); corsConfiguration.setMaxAge(7200L); corsConfiguration.setAllowCredentials(true); return corsConfiguration; } }