Spring Cloud Gateway中路由到https后端
编辑2022-05-13
背景
在进行zuul切换到gateway时,由于我们的微服务都是https的,所以需要在网关进行路由时支持https的调用。
实现方案
参考部分官方文档和技术文章,大概罗列出三种可以实施的方案。
方案一
第一种方案,可以在网关进行路由时,进行如下配置信任所有的下游证书:
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true # 信任所有证书
不过这种方案不安全,不适合正式的生产环境部署。不适用于我们的产品。
方案二
第二种方案,gateway中提供了证书相关的配置,可以使用如下的配置信任证书:
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem
这种方案需要提供pem格式的证书,但是我们的信任库证书都是JKS格式的,需要进行格式转换,这边比较麻烦,也不适合我们的产品。
方案三
参照之前很多定制化修改的实现,我们可以覆盖gateway路由到下游的客户端配置,通过阅读源码发现,gateway启动时会装载一个HttpClientFactory类型的bean,这个factory会创建gateway的httpclient。
具体实现:
/**
* 覆盖gateway默认的httpClient配置 支持jks格式的证书
*
* @author yuanzhihao
* @since 2022/5/12
*/
@Configuration
@Slf4j
public class GatewayHttpClientConfig {
@Value("${server.ssl.trust-store}")
private String trustStore;
@Value("${server.ssl.trust-store-password}")
private String trustStorePassword;
@Value("${server.ssl.trust-store-type}")
private String trustStoreType;
@Bean
@ConditionalOnMissingBean({ HttpClient.class, HttpClientFactory.class })
public HttpClientFactory gatewayHttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties) {
TrustManagerFactory trustManagerFactory = getTrustManagerFactory();
return new CustomHttpClientFactory(properties, serverProperties, Collections.emptyList(), trustManagerFactory);
}
// 加载信任库证书
private TrustManagerFactory getTrustManagerFactory() {
TrustManagerFactory trustManagerFactory = null;
try {
KeyStore keyStore = KeyStore.getInstance(trustStoreType);
try (FileInputStream inStream = new FileInputStream(ResourceUtils.getFile(trustStore))) {
keyStore.load(inStream, trustStorePassword.toCharArray());
}
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
} catch (Exception e) {
log.error("Init TrustManagerFactory Failed", e);
}
return trustManagerFactory;
}
private static class CustomHttpClientFactory extends HttpClientFactory {
private final TrustManagerFactory trustManagerFactory;
public CustomHttpClientFactory(HttpClientProperties properties, ServerProperties serverProperties,
List<HttpClientCustomizer> customizers, TrustManagerFactory trustManagerFactory) {
super(properties, serverProperties, customizers);
this.trustManagerFactory = trustManagerFactory;
}
@SneakyThrows
protected HttpClient configureSsl(HttpClient httpClient) {
HttpClientProperties.Ssl ssl = properties.getSsl();
SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
// 设置信任证书
sslContextBuilder.trustManager(trustManagerFactory);
SslProvider sslProvider = SslProvider.builder()
.sslContext(sslContextBuilder.build())
.handshakeTimeout(ssl.getHandshakeTimeout())
.closeNotifyFlushTimeout(ssl.getCloseNotifyFlushTimeout())
.closeNotifyReadTimeout(ssl.getCloseNotifyReadTimeout())
.build();
httpClient = httpClient.secure(sslProvider);
return httpClient;
}
}
}
结语
对于spring cloud中很多的组件,我们可以通过覆盖他提供的默认的bean来定制我们的功能。
参考地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#tls-and-ssl