Spring Cloud微服务全服务启用SSL后网关请求报TLS要求错误求助
问题场景
基于Spring Cloud Eureka的微服务项目包含注册中心、Cloud Gateway、service-a、service-b四个组件,已为所有组件配置SSL,但通过网关(8849端口)请求service-a/service-b时,收到错误:
Bad Request
This combination of host and port requires TLS.
仅网关启用SSL、禁用服务端SSL时一切正常,同时想了解无服务间通信时,仅网关启用SSL是否为最佳实践。使用版本:Spring Boot 3.3.4、Java 21。
各组件SSL配置如下:
注册中心 application.properties
spring.application.name=serviceregistry server.port=8848 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.instance.prefer-ip-address=false eureka.instance.hostname=localhost eureka.instance.lease-expiration-duration-in-seconds=30 eureka.instance.lease-renewal-interval-in-seconds=15 # SSL server.ssl.enabled=true server.ssl.key-store-type=PKCS12 server.ssl.key-store=<ROOT_PATH>\\sslkey24.p12 server.ssl.key-store-password=PASSWORD server.ssl.key-alias=ALIAS
Cloud Gateway application.yml
server: port: 8849 ssl: enabled: true key-store-type: PKCS12 key-store: <ROOT_PATH>\\sslkey24.p12 key-store-password: PASSWORD key-alias: ALIAS spring: application: name: cloud-gateway cloud: discovery: enabled: true gateway: routes: - id: service-a uri: lb://service-a predicates: - Path=/service-a/** - id: service-b uri: lb://service-b predicates: - Path=/service-b/** eureka: client: register-with-eureka: true fetch-registry: true service-url: defaultZone: <DISCOVERY_SERVER_URL>/eureka instance: hostname: localhost prefer-ip-address: false ip-address: 127.0.0.1 lease-expiration-duration-in-seconds: 30 lease-renewal-interval-in-seconds: 15
Service A application.properties
spring.application.name=service-a server.port=8611 server.servlet.context-path=/service-a server.error.include-binding-errors: always server.error.include-stacktrace=never server.ssl.enabled=true server.ssl.key-store-type=PKCS12 server.ssl.key-store=<ROOT_PATH>\\sslkey24.p12 server.ssl.key-store-password=PASSWORD server.ssl.key-alias=ALIAS # Cloud Gateway eureka.instance.hostname=localhost eureka.instance.prefer-ip-address=false eureka.instance.ip-address=127.0.0.1 eureka.client.serviceUrl.defaultZone=<DISCOVERY_SERVER_URL>/eureka #eureka.instance.secure-port-enabled=true #eureka.instance.secure-port=8611 eureka.instance.lease-expiration-duration-in-seconds=30 eureka.instance.lease-renewal-interval-in-seconds=15
Service B application.properties
spring.application.name=service-b server.port=8612 server.servlet.context-path=/service-b # Cloud Gateway eureka.instance.hostname=localhost eureka.instance.prefer-ip-address=false eureka.instance.ip-address=127.0.0.1 eureka.client.serviceUrl.defaultZone=<DISCOVERY_SERVER_URL>/eureka eureka.instance.lease-expiration-duration-in-seconds=30 eureka.instance.lease-renewal-interval-in-seconds=15 # SSL server.ssl.enabled=true server.ssl.key-store-type=PKCS12 server.ssl.key-store=<ROOT_PATH>\\sslkey24.p12 server.ssl.key-store-password=PASSWORD server.ssl.key-alias=ALIAS
错误根因
service-a、service-b启用SSL后仅监听HTTPS端口,但当前配置中,服务未向Eureka注册安全端口(HTTPS端口),也未标记实例为安全实例。导致网关通过服务发现获取到的是服务的普通HTTP端口地址,用HTTP协议请求已启用SSL的服务,服务端要求TLS连接,因此抛出该错误。
解决方案
1. 配置服务向Eureka注册安全端口
在service-a和service-b的application.properties中,启用安全端口注册并指定端口,同时禁用非安全端口:
# Service A 新增配置 eureka.instance.secure-port-enabled=true eureka.instance.secure-port=8611 eureka.instance.non-secure-port-enabled=false # Service B 新增配置 eureka.instance.secure-port-enabled=true eureka.instance.secure-port=8612 eureka.instance.non-secure-port-enabled=false
配置后Eureka会将服务实例地址注册为HTTPS协议,网关通过lb://service-a调用时会自动使用HTTPS请求服务。
2. 配置网关信任服务端证书(自签名证书场景)
如果使用自签名SSL证书,网关需要配置信任存储来信任服务端证书,否则会出现SSL握手失败。在网关的application.yml中添加:
server: ssl: # 原有SSL配置不变 trust-store-type: PKCS12 trust-store: <ROOT_PATH>\\sslkey24.p12 # 与服务端同一份证书或包含根证书的存储 trust-store-password: PASSWORD
3. 显式指定网关路由协议(可选)
若上述配置后仍有问题,可显式指定网关路由的URI为HTTPS协议:
spring: cloud: gateway: routes: - id: service-a uri: lb:https://service-a predicates: - Path=/service-a/** - id: service-b uri: lb:https://service-b predicates: - Path=/service-b/**
最佳实践解答
当不存在服务间内部通信时,仅在网关启用SSL、禁用内部服务SSL是行业常见的最佳实践,原因如下:
- 降低配置复杂度:无需为每个内部服务配置SSL证书、信任存储等,减少运维成本。
- 减少性能开销:内部网络的SSL握手和加密解密会带来额外性能损耗,内部网络相对安全的情况下可省略。
- 满足外部安全需求:网关作为对外入口,启用SSL即可保证外部通信的安全性,内部服务在可信网络环境下无需额外加密。
但如果内部网络存在安全风险(如多租户环境、跨部门共享网络),或有合规要求(如金融、医疗行业),则需要为内部服务也启用SSL,确保端到端的安全。
内容的提问来源于stack exchange,提问作者Shimul

