1、简介 {#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 {#2spring-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 {#3resttemplate-使用-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 {#4spring-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 {#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 {#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、总结 {#7总结}
本文学习了 Spring Boot 中新的 SSL Bundle 特性,它可以对不同类型的证书进行集中、分组管理,从而可以简化和重用应用中的 SSL 配置。
参考:https://www.baeldung.com/spring-boot-security-ssl-bundles