Spring Cloud Gateway问题
Gateway通过https调用会抛出NotSslRecordException异常问题分析会出现NotSslRecordException异常,网关对外是https,而其他内部调用的服务是http导致。由于服务拆分,在集群中有很多服务生产者和服务的消费者都是在内网中调用,没必要全部https进行调用,通过源码分析LoadBalancerClientFilter#filter()@Over...
·
Gateway通过https调用会抛出NotSslRecordException异常
问题分析
会出现NotSslRecordException异常,网关对外是https,而其他内部调用的服务是http导致。
由于服务拆分,在集群中有很多服务生产者和服务的消费者都是在内网中调用,没必要全部https进行调用,通过源码分析LoadBalancerClientFilter#filter()
@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 || (!"lb".equals(url.getScheme()) && !"lb".equals(schemePrefix))) {
return chain.filter(exchange);
}
//preserve the original url
addOriginalRequestUrl(exchange, url);
log.trace("LoadBalancerClientFilter url before: " + url);
final ServiceInstance instance = choose(exchange);
if (instance == null) {
String msg = "Unable to find instance for " + url.getHost();
if(properties.isUse404()) {
throw new FourOFourNotFoundException(msg);
}
throw new NotFoundException(msg);
}
URI uri = exchange.getRequest().getURI();
// if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
// if the loadbalancer doesn't provide one.
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
URI requestUrl = loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
从源码分析,loadBalancer对http进行封装,如果Spring Cloud Gateway请求进来的是https就用https进行封装,如果请求进来的是http就用http进行封装。
解决方案
在LoadBalancerClientFilter执行之前将https修改为http
@Component
public class Https2HttpFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//请求头
ServerHttpRequest request = exchange.getRequest();
//原始uri
URI originUri = request.getURI();
//构造器
ServerHttpRequest.Builder mutate = request.mutate();
//需要重定向的uri
String forwardedUri = originUri.toString();
if (StringUtils.startsWith(forwardedUri, "https")) {
try {
//重新生成http请求方式的uri
URI uri = new URI(
"http",
originUri.getUserInfo(),
originUri.getHost(),
originUri.getPort(),
originUri.getPath(),
originUri.getQuery(),
originUri.getFragment()
);
mutate.uri(uri);
} catch (URISyntaxException e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
//重新构建
ServerHttpRequest build = mutate.build();
return chain.filter(exchange.mutate().request(build).build());
}
@Override
public int getOrder() {
return LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 1;
}
}
在LoadBalancerClientFilter执行之后将https修改为http
@Component
public class HttpSchemeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取uri
Object uriObj = exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
if (uriObj instanceof URI) {
URI uri = (URI) uriObj;
//替换为http
uri = upgradeConnection(uri, "http");
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri);
}
return chain.filter(exchange);
}
/**
* 更新scheme
*
* @param uri
* @param scheme
* @return
*/
private URI upgradeConnection(URI uri, String scheme) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUri(uri).scheme(scheme);
if (!StringUtils.isEmpty(uri.getRawQuery())) {
builder.replaceQueryParam(uri.getRawQuery().replace("+", "%20"));
}
return builder.build(true).toUri();
}
@Override
public int getOrder() {
return LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER + 1;
}
}
更多推荐
已为社区贡献1条内容
所有评论(0)