Spring Boot接收HTTPS请求转发至HTTP服务及相关TLS报错解决咨询
报错根因
“This combination of host and port required TLS”报错通常由以下两种原因触发:
- 你配置的下游服务端口为HTTPS默认的443,但请求协议指定为
http://,客户端默认检测到443端口会自动发起TLS握手,与指定的HTTP协议冲突- 你使用的HTTP请求客户端(RestTemplate/WebClient/OpenFeign)被全局配置了强制启用TLS,哪怕目标地址为HTTP协议也会尝试TLS握手
可行实现方案
以下给出两种生产可用的实现方式,按需选择即可:
方案1:Spring Cloud Gateway 实现(推荐,无需手写转发逻辑)
Spring Cloud Gateway 自带请求、响应透传能力,配置即可用,适合高并发转发场景:
- 第一步引入网关依赖(Maven示例):
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>适配你的Spring Boot版本的最新稳定版</version> </dependency>
- 第二步编写
application.yml配置:
# 自身HTTPS服务配置(你已经配置好的部分可保留) server: port: 443 ssl: enabled: true key-store: classpath:你的证书文件.p12 key-store-password: 你的证书密码 key-alias: 证书别名 spring: cloud: gateway: routes: - id: https-to-http-forward uri: http://下游HTTP服务的IP或域名:端口 # 必须明确加http://前缀,不能省略 predicates: - Path=/** # 匹配所有入站请求,可按需调整匹配规则 filters: - RemoveRequestHeader=Host # 可选,避免原请求的Host头导致下游服务校验失败 httpclient: ssl: enabled: false # 核心配置:请求下游时默认不启用TLS,仅当目标uri为https时自动启用
方案2:RestTemplate 手动实现转发(轻量场景适用)
如果不想引入网关组件,可手动写转发逻辑透传请求和响应:
- 第一步声明专门用于HTTP请求的RestTemplate Bean,不要复用配置了SSL的全局实例:
@Configuration public class HttpRestTemplateConfig { @Bean public RestTemplate plainHttpRestTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(5000); factory.setReadTimeout(30000); return new RestTemplate(factory); } }
- 第二步编写转发接口:
@RestController public class ForwardController { @Resource private RestTemplate plainHttpRestTemplate; @RequestMapping("/**") public ResponseEntity<byte[]> forward(HttpServletRequest request, @RequestBody(required = false) byte[] requestBody) { // 拼接下游请求地址 StringBuilder targetUrl = new StringBuilder("http://下游HTTP服务的IP或域名:端口") .append(request.getRequestURI()); if (request.getQueryString() != null) { targetUrl.append("?").append(request.getQueryString()); } // 透传请求方法 HttpMethod method = HttpMethod.valueOf(request.getMethod()); // 透传请求头 HttpHeaders headers = new HttpHeaders(); Collections.list(request.getHeaderNames()).forEach(headerName -> { if (!"host".equalsIgnoreCase(headerName)) { headers.add(headerName, request.getHeader(headerName)); } }); // 发起请求并透传响应 HttpEntity<byte[]> httpEntity = new HttpEntity<>(requestBody, headers); return plainHttpRestTemplate.exchange(targetUrl.toString(), method, httpEntity, byte[].class); } }
特殊场景排查
如果你的下游HTTP服务确实使用443端口,需要额外配置客户端禁用端口的TLS自动检测,以Apache HttpClient替换RestTemplate默认实现为例:
@Bean public RestTemplate plainHttpRestTemplate() { Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", SSLConnectionSocketFactory.getSocketFactory()) .build(); PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry); HttpClient httpClient = HttpClientBuilder.create() .setConnectionManager(cm) .disableAutomaticRetries() .build(); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); factory.setConnectTimeout(5000); factory.setReadTimeout(30000); return new RestTemplate(factory); }
内容的提问来源于stack exchange,提问作者Dipisha jain
相关产品推荐
相关产品推荐

