拓展spring cloud gateway 動態路由解析及302問題
阿新 • • 發佈:2019-03-26
1.動態路由 需求:根據指定內網服務的ip和埠資訊提供反向代理。
原內網服務是釋出到外網上,通過業務邏輯在前端進行整合。後需要將這些內網服務隱藏到主服務之後。
第一版是通過自寫的netty進行實現。後改用spring cloud gateway進行實現。以方便後續維護。
實現效果:
- innerhost1.innerport1.proxy.domain.com 訪問 內網服務1、innerhost2.innerport2.proxy.domain.com 訪問 內網服務2;
- innerhost:內網服務地址 innerport 內網服務埠。
eg:127.0.0.1.8080.dev.localhost TO 127.0.0.1:8080
思路:參照gateway lb :RouteToRequestUrlFilter實現。
/** * route根據predicates配置進行匹配。因此只要制定符合需求的predicates便可獲得對應route及其uri。 */ RoutePredicateHandlerMapping.lookupRoute(ServerWebExchange exchange):Mono<Route> /** * 在filter中 RouteToRequestUrlFilter將route.uri解析並存儲到 ServerWebExchange.getAttributes():GATEWAY_REQUEST_URL_ATTR 中 因此只要proxyfilter在RouteToRequestUrlFilter之後執行即可直接使用該uri */ RouteToRequestUrlFilter.filter(ServerWebExchange exchange, GatewayFilterChain chain):Mono<Void>
實現:
package.component: @Component public class ProxyFilter implements GlobalFilter, Ordered { @Override public int getOrder() { // 定義在RouteToRequestUrlFilter之後執行 return RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 1000; } /** * 參照RouteToRequestUrlFilter實現 */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR); if (url == null || (!"proxy".equals(url.getScheme()) && !"proxy".equals(schemePrefix))) { return chain.filter(exchange); } addOriginalRequestUrl(exchange, url); // TODO 根據配置中的beanName對url進行處理 String beanName = url.getHost(); IProxy proxy = null; try { proxy = SpringContextUtils.getBean(beanName); } catch (Exception e) { log.error("異常{}", e.getLocalizedMessage()); } if (proxy == null) { log.error("異常URI{} 解析:N{}", url, beanName); return chain.filter(exchange); } URI requestUrl = proxy.getProxyUri(exchange); if (requestUrl == null) { log.error("異常URI{} 解析:N{}", url, beanName); requestUrl = url; } exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl); Mono<Void> mono = chain.filter(exchange); proxy.beforeSend(exchange); return mono; } } package.component: public interface IProxy { public URI getProxyUri(ServerWebExchange exchange); public default void beforeSend(ServerWebExchange exchange) { } } package.domain: @Configuration public class ProxyRemote { private static URI error404URI; static { try { error404URI = new URI("http://www.localhost/common/404/404.html"); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Bean public IProxy dev() { return exchange -> { URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); if (uri == null) return error404URI; String host = exchange.getRequest().getURI().toString(); if (StringUtils.isEmpty(host) && host.contains("://") && host.contains(".dev")) { log.error("異常URI{} .", host); return error404URI; } int i; host = host.substring((i = host.indexOf("://") + 3), host.indexOf(".dev", i)); if (!host.contains(".")) { return error404URI; } String port = host.substring((i = host.lastIndexOf(".")) + 1); host = host.substring(0, i); if (!NetUtils.isIp(host)) { return error404URI; } URI newUri; try { newUri = new URI("http://" + host + ":" + port + uri.getPath().toString() + (Objs.isEmpty(uri.getQuery()) ? "" : "?" + uri.getQuery())); log.info("ASK:{},R:{}", exchange.getRequest().getURI().toString(), newUri); return newUri; } catch (URISyntaxException e) { e.printStackTrace(); } return error404URI; }; } }
application.yml中配置如下:
proxyBeanName:
dev: dev
spring:
cloud:
gateway:
routes:
- id: proxy-dev
uri: proxy://${proxyBeanName.dev:dev}
predicates:
- Host=**.${proxyBeanName.dev:dev}.**
github: https://github.com/yuyizyk/note/tree/master/notes/spring-cloud-gateway-enhance
2.302問題 在gateway代理子服務中,一但發生302重定向:response.sendRedirect("/~");
那麼在放回的resp的header.location 中可以發現該重定向路徑是其內網服務地址(根據gateway代理訪問地址)自動補全後的路徑。而並非是gateway所代表的公網服務路徑。
參照:https://blog.csdn.net/tianyaleixiaowu/article/details/83618037
將其響應的header.location進行修改。即可完成處理302問題。
/**
* 修改 302 返回host
*
*
*
* @author yuyi
*/
@Slf4j
@Component
public class WrapperResponseGlobalFilter implements GlobalFilter, Ordered {
@Override
public int getOrder() {
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpResponse originalResponse = exchange.getResponse();
URI uri = exchange.getRequest().getURI();
// DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@SuppressWarnings("serial")
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
switch (getDelegate().getStatusCode().value()) {
case 302:
case 301:
HttpHeaders headers = getDelegate().getHeaders();
String location = headers.getFirst(HttpHeaders.LOCATION);
int i = -1;
if (StringUtils.isEmpty(location) || (i = location.indexOf("/", 8)) == -1) {
log.error("異常URI{} .", location);
break;
}
String newLocation = uri.getScheme() + "://" + uri.getHost() + ":"
+ (uri.getPort() > 0 ? uri.getPort() : 80) + location.substring(i);
log.debug("ASK {} for RELP URL :{} TO {} ", uri.toString(), location, newLocation);
headers.put(HttpHeaders.LOCATION, new ArrayList<String>() {
{
add(newLocation);
}
});
break;
default:
break;
}
return super.writeWith(body);
}
};
// replace response with decorator
return chain.filter(exchange.mutate().response(decoratedResponse).bui