Spring Boot 中 SSL Bundle 的用法
1、简介
以前在 Spring Boot 中配置 SSL 非常复杂,主要是证书有很多类型,如:JKS、PKCS #12 或 PEM。每种类型的配置方式又不一样。
幸运的是,Spring Boot 3.1 引入了 SSL Bundle,旨在简化 Spring Boot 中的 SSL 配置。在本教程中,我们将了解什么是 SSL Bundle,以及如何用它简化 Spring Boot 应用中的 SSL 配置。
2、Spring Boot SSL Bundle
通常,我们需要把 SSL 证书转换为可用的 Java 对象。
java.security.KeyStore
对象用于存储证书。javax.net.ssl.KeyManager
对象用于管理密钥。javax.net.ssl.SSLContext
对象用于创建安全的套接字连接(Socket Connection)。
每个类都需要更深入的理解和配置,使得整个过程变得繁琐且容易出错。各种 Spring Boot 组件可能还需要深入到不同的抽象层来应用这些设置,给任务增加了另一个难度层级。
SSL Bundle 将所有 SSL 的配置(如 Keystore、证书和私钥)封装成一个易于管理的单元。可以应用于一个或多个网络连接,无论它们是传入连接(嵌入式服务器)还是传出连接(HTTP 客户端)。
SSL Bundle 在 application.yaml
或 application.properties
中,配置属性前缀是 spring.ssl.bundle
。
首先从 JKS Bundle 开始,使用 spring.ssl.bundle.jks
来配置 Java Keystore 证书:
spring:
ssl:
bundle:
jks:
server:
key:
alias: "server"
keystore:
location: "classpath:server.p12"
password: "secret"
type: "PKCS12"
对于 PEM Bundle,使用 spring.ssl.bundle.pem
来配置 PEM 编码的证书:
spring:
ssl:
bundle:
pem:
client:
truststore:
certificate: "classpath:client.crt"
一旦配置好了这些 Bundle 后,它们就可以用于 Spring Boot 中的各种组件,如:嵌入式服务器、HTTP 客户端(RestTemplate、WebClient)、数据库连接(Redis、MongoDB …)以及自己编码创建 SSL 连接。
Spring Boot 可根据 SSL Bundle 配置自动创建 KeyStore
、KeyManager
和 SSLContext
等 Java 对象。不需要手动创建和管理这些对象,使创建过程更加简单明了,不易出错。
3、RestTemplate
使用 SSL Bundles 配置 SSL
首先介绍 RestTemplate
如何通过 SSL Bundle 配置 SSL。
首先,生成用于 SSL Bundle 的密钥。
使用 openssl
(用 Git 也可以)生成密钥,在项目根目录下执行以下命令:
$ openssl req -x509 -newkey rsa:4096 -keyout src/main/resources/key.pem -out src/main/resources/cert.pem -days 365 -passout pass:FooBar
然后,把密钥转换成 PKCS12 格式:
$ openssl pkcs12 -export -in src/main/resources/cert.pem -inkey src/main/resources/key.pem -out src/main/resources/keystore.p12 -name secure-service -passin pass:FooBar -passout pass:FooBar
现在,在 application.yml
文件中定义一个名为 secure-service
的 Bundle:
spring:
ssl:
bundle:
jks:
secure-service:
key:
alias: "secure-service"
keystore:
location: "classpath:keystore.p12"
password: "FooBar"
type: "PKCS12"
接下来,可以通过调用 RestTemplateBuilder
的 setSslBundle()
方法在 RestTemplate
上设置 Bundle:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
return restTemplateBuilder.setSslBundle(sslBundles.getBundle("secure-service")).build();
}
最后,可以使用配置好的 RestTemplate
Bean 调用 API:
@Service
public class SecureServiceRestApi {
private final RestTemplate restTemplate;
@Autowired
public SecureServiceRestApi(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public String fetchData(String dataId) {
ResponseEntity<String> response = restTemplate.exchange(
"https://secure-service.com/api/data/{id}",
HttpMethod.GET,
null,
String.class,
dataId
);
return response.getBody();
}
}
本示例中的 SSL Bundle 用于验证 secure-service
的证书,确保了一个加密和安全的通信通道。然而,这并不限制我们在 API 端使用客户端证书进行身份验证。稍后我们将看到如何获取 SSLContext
来配置自定义客户端。
4、Spring Boot 自动配置的 SSLBundle
在 Spring Boot 引入 SSL Bundle 之前,开发者使用经典 Java 的类来配置 SSL:
java.security.KeyStore
:用于 Keystore 和 Truststore,实际上是加密密钥和证书的存储库。javax.net.ssl.KeyManager
和javax.net.ssl.TrustManager
:这些实例分别管理 SSL 通信过程中的密钥和信任决策。javax.net.ssl.SSLContext
:这些实例充当SSLEngine
和SSLSocket
对象的工厂,协调运行时 SSL 配置的实现方式。
Spring Boot 3.1 引入了一个结构化的抽象层,分为多个 Java 接口:
SslStoreBundle
:为包含加密密钥和证书的KeyStore
对象提供访问方法。SslManagerBundle
:协调并提供管理KeyManager
和TrustManager
对象的方法。SslBundle
:提供一站式服务,将所有这些功能整合到与 SSL 生态系统的统一交互模型中。
Spring Boot 会自动配置一个 SslBundles
Bean。可以方便地将其注入到任何 Spring Bean 中。这非常有用,特别是为传统代码、自定义 REST 客户端配置 SSL 时。
例如,假设我们需要为 HttpClient
配置一个自定义的 SSLContext
:
@Component
public class SecureRestTemplateConfig {
private final SSLContext sslContext;
@Autowired
public SecureRestTemplateConfig(SslBundles sslBundles) throws NoSuchSslBundleException {
SslBundle sslBundle = sslBundles.getBundle("secure-service");
this.sslContext = sslBundle.createSslContext();
}
@Bean
public RestTemplate secureRestTemplate() {
SSLConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactoryBuilder.create().setSslContext(this.sslContext).build();
HttpClientConnectionManager cm = PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslSocketFactory).build();
HttpClient httpClient = HttpClients.custom().setConnectionManager(cm).evictExpiredConnections().build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
}
在上面的代码中,我们通过构造函数(@Autowired
)注入 SslBundles
实例。通过 SslBundles
可以访问所有已配置的 SSL Bundle。因此,我们检索 secure-service
Bundle 并创建 SSLContext
。然后,我们使用 SSLContext
实例创建一个自定义的 HttpClient
,并将其用于创建 RestTemplate
Bean。
5、数据库连接使用 SSL Bundle 配置 SSL
不同的数据库连接,有不同的 SSL 配置选项,这就导致了配置比较复杂。
SSL Bundles 为各种数据库连接的 SSL 配置引入了一种更统一的方法:
- Cassandra:
spring.cassandra.ssl
- Couchbase:
spring.couchbase.env.ssl
- Elasticsearch:
spring.elasticsearch.restclient.ssl
- MongoDB:
spring.data.mongodb.ssl
- Redis:
spring.data.redis.ssl
目前,大多数连接都支持 .ssl.enabled
属性。该属性可激活客户端库中的 SSL 支持,使用的是 Java 运行时 cacerts
中的证书。
现在,可以使用 *.ssl.bundle
指定一个已配置的 SSL Bundle 来启用 SSL 支持,从而实现多个连接的统一配置和 Bundle 的重用。
假如,我们有一个名为 mongodb-ssl-bundle
的 SSL Bundle。该 Bundle 配置了必要的证书,可确保与 MongoDB 实例的连接安全。
那么,在 application.yml
文件中 MongoDB 连接的 SSL 配置如下:
spring:
data:
mongodb:
ssl:
enabled: true
bundle: mongodb-ssl-bundle
如此,Spring Boot 中的 MongoDB 客户端库就会自动使用 mongodb-ssl-bundle
中指定的证书来创建 SSL 连接。
6、嵌入式服务器使用 SSL Bundles 配置 SSL
使用 SSL Bundle 还可以简化 Spring Boot 中嵌入式 Web 服务器(Tomcat、Undertow、Jetty)的 SSL 配置和管理。
传统上,通过 server.ssl.*
属性单独为 Web 服务配置 SSL 证书。有了 SSL Bundle,就可以将证书配置分组,然后在多个地方重复使用,从而减少出错的机会。
传统的服务器证书配置方式如下:
server:
ssl:
key-alias: "server"
key-password: "keysecret"
key-store: "classpath:server.p12"
key-store-password: "storesecret"
client-auth: NEED
现在,可以先使用 SSL Bundle 配置证书:
spring:
ssl:
bundle:
jks:
web-server:
key:
alias: "server"
password: "keysecret"
keystore:
location: "classpath:server.p12"
password: "storesecret"
然后在服务器的 SSL 配置中,直接指定已配置的 SSL Bundle:
server:
ssl:
bundle: "web-server"
client-auth: NEED
使用 SSL Bundle 的方式在配置管理方面更有效。因为 Bundle 配置的证书还可以同时用于其他组件,如 management.server.ssl
和 spring.rsocket.server.ssl
。
7、总结
本文学习了 Spring Boot 中新的 SSL Bundle 特性,它可以对不同类型的证书进行集中、分组管理,从而可以简化和重用应用中的 SSL 配置。
参考:https://www.baeldung.com/spring-boot-security-ssl-bundles