package com.github.zuihou.gateway.config; import feign.codec.Decoder; import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator; import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties; import org.springframework.cloud.gateway.route.RouteDefinitionLocator; import org.springframework.cloud.openfeign.support.ResponseEntityDecoder; import org.springframework.cloud.openfeign.support.SpringDecoder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.codec.support.DefaultServerCodecConfigurer; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.client.RestTemplate; import org.springframework.web.cors.reactive.CorsUtils; import org.springframework.web.server.WebFilter; import reactor.core.publisher.Mono; import java.util.List; /** * 解决跨域问题 * * @author zuihou * @date 2017-12-29 14:16 */ @Configuration public class CorsConfiguration { private static final String ALL = "*"; private static final String MAX_AGE = "18000L"; @Bean public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) { return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties); } /** * attention:简单跨域就是GET,HEAD和POST请求,但是POST请求的"Content-Type"只能是application/x-www-form-urlencoded, multipart/form-data 或 text/plain * 反之,就是非简单跨域,此跨域有一个预检机制,说直白点,就是会发两次请求,一次OPTIONS请求,一次真正的请求 */ @Bean public WebFilter corsFilter() { return (ctx, 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); }; } @Bean public ServerCodecConfigurer serverCodecConfigurer() { return new DefaultServerCodecConfigurer(); } /** * 升级版本后, 不加这个 gateway 使用feign会报错,不知道什么原因 * * @return */ @Bean public Decoder feignFormDecoder() { List<HttpMessageConverter<?>> converters = new RestTemplate().getMessageConverters(); ObjectFactory<HttpMessageConverters> factory = () -> new HttpMessageConverters(converters); return new ResponseEntityDecoder(new SpringDecoder(factory)); } }