Spring Cloud Gateway problems

Keywords: Spring

The Gateway will throw a NotSslRecordException exception through the https call

problem analysis

There will be a NotSslRecordException exception. The gateway is https externally, while other internally called services are caused by http.

Due to service splitting, there are many service producers and service consumers in the cluster calling in the intranet. It is not necessary to call all https, and analyze LoadBalancerClientFilter#filter() through source code.

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

From the source code analysis, the loadBalancer encapsulates http. If the Spring Cloud Gateway requests https, it will encapsulate it with https. If the Spring Cloud Gateway requests HTTP, it will encapsulate it with HTTP.

Solution

Modify https to http before LoadBalancerClientFilter executes

@Component
public class Https2HttpFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //Request header
        ServerHttpRequest request = exchange.getRequest();
        //Original uri
        URI originUri = request.getURI();
        //constructor
        ServerHttpRequest.Builder mutate = request.mutate();
        //uri to redirect
        String forwardedUri = originUri.toString();
        if (StringUtils.startsWith(forwardedUri, "https")) {
            try {
                //Regenerate the uri of http request mode
                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);
            }

        }
        //Rebuild
        ServerHttpRequest build = mutate.build();
        return chain.filter(exchange.mutate().request(build).build());
    }

    @Override
    public int getOrder() {
        return LoadBalancerClientFilter.LOAD_BALANCER_CLIENT_FILTER_ORDER - 1;
    }
}

Modify https to http after LoadBalancerClientFilter is executed

@Component
public class HttpSchemeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //Get uri
        Object uriObj = exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        if (uriObj instanceof URI) {
            URI uri = (URI) uriObj;
            //Replace with http
            uri = upgradeConnection(uri, "http");
            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, uri);
        }

        return chain.filter(exchange);
    }

    /**
     * Update 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;
    }
}

Posted by MnilinM on Mon, 09 Dec 2019 08:21:04 -0800