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