Spring Cloud Gateway问题
程序员文章站
2022-06-03 20:43:37
...
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;
}
}
上一篇: 偷拍
下一篇: 看好2008的*IT市场
推荐阅读
-
详解配置spring-boot-actuator时候遇到的一些小问题
-
spring boot创建项目包依赖问题的解决
-
浅谈Spring Cloud下微服务权限方案
-
Spring Cloud 动态刷新配置信息教程详解
-
关于Spring MVC同名参数绑定问题的解决方法
-
Spring Cloud 配置中心内容加密的配置方法
-
Spring Cloud出现Options Forbidden 403问题解决方法
-
spring boot 打包jar jar没有主目录清单问题的完美解决方法
-
Spring Cloud Alibaba Nacos 入门详解
-
spring cloud consul使用ip注册服务的方法示例