1. 简介
管理Spring Boot应用程序中的安全通信通常涉及处理复杂的配置。挑战通常始于处理信任材料,例如证书和私钥,这些材料采用各种格式,如JKS、PKCS #12或PEM。对于如何处理这些格式,每种格式都有自己的一套要求。
幸运的是,Spring Boot 3.1引入了SSL Bundles,这一功能旨在简化这些复杂性。在本教程中,我们将探讨什么是SSL捆绑包以及它们如何简化Spring Boot应用程序的SSL配置任务。
2. Spring Boot SSL捆绑包
通常,一旦我们获得了信任材料,我们需要将其转换为应用程序可以使用的Java对象。这通常意味着处理诸如用于存储密钥材料的java.security.KeyStore、用于管理密钥材料的javax.net.ssl.KeyManager以及用于创建安全套接字连接的javax.net.ssl.SSLContext之类的类。
每个类都需要另一层理解和配置,使得该过程繁琐且容易出错。各种Spring Boot组件可能还需要深入研究不同的抽象层来应用这些设置,从而给任务增加了另一层难度。
SSL捆绑包将所有信任材料和配置设置(例如密钥库、证书和私钥)封装到一个易于管理的单元中,配置SSL捆绑包后,即可将其应用于一个或多个网络连接,无论它们是传入还是传出。
SSL捆绑包的配置属性位于application.yaml或application.properties配置文件中的spring.ssl.bundle前缀下。
让我们从JKS捆绑包开始。我们使用spring.ssl.bundle.jks来配置使用JavaKeystore文件的包:
spring:
ssl:
bundle:
jks:
server:
key:
alias: "server"
keystore:
location: "classpath:server.p12"
password: "secret"
type: "PKCS12"
对于PEM捆绑包,我们使用spring.ssl.bundle.pem来使用PEM编码的文本文件配置捆绑包:
spring:
ssl:
bundle:
pem:
client:
truststore:
certificate: "classpath:client.crt"
配置这些捆绑包后,它们可以跨微服务应用-无论是需要安全访问数据库的库存服务、需要安全API调用的用户身份验证服务,还是与支付网关安全通信的支付处理服务。
Spring Boot根据SSL Bundle配置自动创建Java对象,如KeyStore、KeyManager和SSLContext。这消除了手动创建和管理这些对象的需要,使过程更加简单并且不易出错。
3. 使用SSL捆绑包保护RestTemplate
让我们从在使用RestTemplate Bean的同时利用SSL捆绑包开始。为此,我们将使用示例Spring Boot应用程序,但首先,我们需要生成将用作SSL捆绑包的密钥。
我们将使用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
因此,我们拥有配置SSL捆绑包的一切;让我们在application.yml文件中定义一个名为“secure-service”的捆绑包:
spring:
ssl:
bundle:
jks:
secure-service:
key:
alias: "secure-service"
keystore:
location: "classpath:keystore.p12"
password: "FooBar"
type: "PKCS12"
接下来,我们可以通过调用setSslBundle()方法在RestTemplate上设置捆绑包:
@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();
}
}
Spring Boot应用程序中的SSL Bundle用于验证secure-service的证书,确保加密且安全的通信通道。但是,这并不限制我们在API端使用客户端证书进行身份验证。稍后我们将看到如何获取SSLContext来配置自定义客户端。
4. 利用Spring Boot的自动配置SSLBundles
在Spring Boot的SSL Bundles之前,开发人员通常使用支持SSL配置的经典Java类:
- java.security.KeyStore:这些实例用作密钥库和信任库,有效地充当加密密钥和证书的安全存储库。
- 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。因此,我们可以方便地将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捆绑包的访问。因此,我们检索安全服务包并创建上下文。稍后,我们使用SSLContext实例创建自定义HttpClient并应用它来创建RestTemplate Bean。
5. 将SSL捆绑包与数据服务结合使用
不同的数据服务具有不同程度的SSL配置选项,从而在配置过程中造成复杂性。
SSLBundles引入了一种更统一的方法来跨各种数据服务进行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捆绑包来自定义信任材料,从而实现跨多个服务连接的一致性和可重用性。
对于此示例,我们假设有一个名为mongodb-ssl-bundle的SSL包。该捆绑包包含必要的信任材料,以确保与MongoDB实例的连接安全。
让我们定义application.yml文件:
spring:
data:
mongodb:
ssl:
enabled: true
bundle: mongodb-ssl-bundle
只需添加这些属性,Spring Boot应用程序中的MongoDB客户端库就会自动使用mongodb-ssl-bundle中指定的SSL上下文和信任材料。
6. 将SSL捆绑包与嵌入式服务器一起使用
使用SSL捆绑包还可以简化在Spring Boot中管理嵌入式Web服务器的SSL配置。
传统上,server.ssl.*属性用于设置每个单独的SSL配置。通过SSL捆绑包,可以将配置分组在一起,然后在多个连接之间重复使用,从而减少出错的可能性并简化整体管理。
首先,让我们探讨一下传统的方法:
server:
ssl:
key-alias: "server"
key-password: "keysecret"
key-store: "classpath:server.p12"
key-store-password: "storesecret"
client-auth: NEED
另一方面,SSLBundle方法允许封装相同的配置:
spring:
ssl:
bundle:
jks:
web-server:
key:
alias: "server"
password: "keysecret"
keystore:
location: "classpath:server.p12"
password: "storesecret"
现在,我们可以使用定义的包来保护我们的Web服务器:
server:
ssl:
bundle: "web-server"
client-auth: NEED
虽然这两种方法都可以保护嵌入式Web服务器,但SSL捆绑方法对于配置管理来说更有效。此功能也可用于其他配置,例如management.server.ssl和spring.rsocket.server.ssl。
7. 总结
在本文中,我们探索了Spring Boot中新的SSLBundles功能,该功能可以简化和统一配置信任材料的过程。
与传统的server.ssl.*属性相比,SSL捆绑包提供了一种结构化的方式来管理密钥库和信任库。这对于减少错误配置风险和提高跨多个服务管理SSL的效率特别有利。
此外,SSL捆绑包非常适合集中管理,允许在应用程序的不同部分重复使用同一个捆绑包。
通过合并SSL捆绑包,开发人员不仅可以简化配置过程,还可以提升Spring Boot应用程序中嵌入式Web服务器的安全状况。
Post Directory
