欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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市场