1. 简介
在Spring Boot应用中管理安全通信通常涉及复杂的配置。挑战始于处理各种格式的信任材料(如JKS、PKCS #12或PEM格式的证书和私钥),每种格式都有特定的处理要求。
幸运的是,Spring Boot 3.1引入了SSL Bundles特性,旨在简化这些复杂性。本文将探讨SSL Bundles是什么,以及它们如何简化Spring Boot应用的SSL配置任务。
2. Spring Boot SSL Bundles
通常,获取信任材料后需要将其转换为应用可用的Java对象。这意味着要处理以下类:
java.security.KeyStore
:存储密钥材料javax.net.ssl.KeyManager
:管理密钥材料javax.net.ssl.SSLContext
:创建安全套接字连接
每个类都需要额外的理解和配置层,使过程繁琐且容易出错。各种Spring Boot组件可能还需要深入不同抽象层来应用这些设置,进一步增加难度。
SSL Bundle将所有信任材料和配置设置(如密钥库、证书和私钥)封装到单个易管理的单元中。配置后,可应用于一个或多个网络连接(入站或出站)。
SSL Bundles的配置属性位于application.yaml
或application.properties
的spring.ssl.bundle
前缀下。
2.1 JKS Bundles配置
使用spring.ssl.bundle.jks
配置基于Java密钥库文件的Bundle:
spring:
ssl:
bundle:
jks:
server:
key:
alias: "server"
keystore:
location: "classpath:server.p12"
password: "secret"
type: "PKCS12"
2.2 PEM Bundles配置
使用spring.ssl.bundle.pem
配置基于PEM编码文本文件的Bundle:
spring:
ssl:
bundle:
pem:
client:
truststore:
certificate: "classpath:client.crt"
配置后,这些Bundle可在微服务间复用:
- 库存服务安全访问数据库
- 用户认证服务进行安全API调用
- 支付处理服务与支付网关安全通信
Spring Boot会根据SSL Bundle配置自动创建KeyStore
、KeyManager
和SSLContext
等Java对象,无需手动管理,使过程更简单且不易出错。
3. 使用SSL Bundles保护RestTemplate
让我们通过示例Spring Boot应用,了解如何在使用RestTemplate
Bean时利用SSL Bundles。首先需要生成用作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"
通过调用setSslBundle()
方法将Bundle应用到RestTemplate
:
@Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
return restTemplateBuilder.setSslBundle(sslBundles.getBundle("secure-service")).build();
}
使用配置好的RestTemplate
调用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();
}
}
Spring Boot应用中的SSL Bundle用于验证secure-service
的证书,确保加密安全通信通道。但这并不限制我们在API端使用客户端证书进行身份验证,后续将展示如何获取SSLContext
配置自定义客户端。
4. 利用Spring Boot自动配置的SSLBundles
在SSL Bundles出现前,开发者需要处理支撑SSL配置的经典Java类:
java.security.KeyStore
:用作密钥库和信任库,作为密钥和证书的安全存储库javax.net.ssl.KeyManager
/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,因此我们可以方便地将SslBundle
实例注入任何Spring Bean。这对遗留代码库和自定义REST客户端的安全通信配置特别有用。
例如,为自定义安全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);
}
}
上述代码中,我们将SslBundles
实例注入@Autowired
构造函数。SslBundles
提供对所有已配置SSL Bundles的访问,因此我们获取secure-service
Bundle并创建上下文,然后使用SSLContext
实例创建自定义HttpClient
并应用于RestTemplate
Bean。
5. 在数据服务中使用SSL Bundles
不同数据服务的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来自定义信任材料,从而在多个服务连接间实现统一性和可重用性。
假设存在名为mongodb-ssl-bundle
的SSL Bundle,包含安全连接到MongoDB实例所需的信任材料。在application.yml
中定义:
spring:
data:
mongodb:
ssl:
enabled: true
bundle: mongodb-ssl-bundle
通过简单添加这些属性,Spring Boot应用中的MongoDB客户端库会自动使用mongodb-ssl-bundle
中指定的SSL上下文和信任材料。
6. 在嵌入式服务器中使用SSL Bundles
使用SSL Bundles也可简化Spring Boot嵌入式Web服务器的SSL配置管理。
传统上使用server.ssl.*
属性设置每个SSL配置项。而SSL Bundles允许将配置分组并在多个连接间复用,减少错误并简化整体管理。
6.1 传统属性方式
server:
ssl:
key-alias: "server"
key-password: "keysecret"
key-store: "classpath:server.p12"
key-store-password: "storesecret"
client-auth: NEED
6.2 SSL Bundle方式
首先封装配置:
spring:
ssl:
bundle:
jks:
web-server:
key:
alias: "server"
password: "keysecret"
keystore:
location: "classpath:server.p12"
password: "storesecret"
然后使用Bundle保护Web服务器:
server:
ssl:
bundle: "web-server"
client-auth: NEED
两种方式都能保护嵌入式Web服务器,但SSL Bundle方法在配置管理上更高效。该特性同样适用于management.server.ssl
和spring.rsocket.server.ssl
等其他配置。
7. 结论
本文探讨了Spring Boot中新的SSL Bundles特性,它能简化和统一信任材料的配置过程。
与传统server.ssl.*
属性相比,SSL Bundles提供了结构化的密钥库和信任库管理方式,特别有助于减少配置错误风险,提高跨服务SSL管理效率。
此外,SSL Bundles非常适合集中管理,允许同一Bundle在应用不同部分复用。
通过整合SSL Bundles,开发者不仅能简化配置过程,还能提升Spring Boot应用中嵌入式Web服务器的安全态势。
完整代码示例可在GitHub获取。