Spring Cloud Gateway问题

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;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_16782189/article/details/88941501