领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多安全套接字层 (SSL) 和传输层安全 (TLS) 是保护分层或面向服务的体系结构中系统之间通信的关键组件。这种体系结构中的 Spring Boot 应用程序通常接受传入的网络连接或创建传出连接,开发人员的任务是配置应用程序以在这样的安全环境中工作。
如果您曾经使用过 Java 安全和 SSL API,您可能知道这并不是一项特别有趣的任务。它通常需要多次访问stackoverflow.com 来复制和粘贴代码。有几个因素导致使用 SSL 变得痛苦。
首先,您可能会收到用于生产用途的证书和私钥等信任材料。您可能需要为生产前测试生成不同的信任材料(通常使用自签名证书颁发机构)。此信任材料通常以 JKS 或 PKCS #12 格式的 Java 密钥库文件的形式存在,或者它们可能是 PEM 编码的文本文件。每种文件类型都需要不同的处理方式。
获得信任材料后,您需要将其转换为可以传递给 Java 连接 API 的内容。事情可能会变得困难,因为连接 API 可以通过多种方式进行配置。
java.security.KeyStore
实例。javax.net.ssl.KeyManager
和javax.net.ssl.TrustManager
实例。javax.net.ssl.SSLContext
实例。SSL 也是非常底层的,因此您通常需要剥离几层抽象才能到达需要使用java.security
或java.net.ssl
包中的对象进行配置的内容。例如,如果您想在 SpringRestTemplate
上配置 SSL,则需要深入到支持它的ClientHttpRequestFactory
。对于典型的 Spring Boot 应用程序,这可能是HttpComponentsClientHttpRequestFactory
、OkHttp3ClientHttpRequestFactory
或SimpleClientHttpRequestFactory
。这些都提供了不同的配置 API。
为连接配置使用 SSL 或 TLS 对 Spring Boot 来说并不新鲜,但团队决定对当前支持的内容进行整体查看,并寻找改进和扩展支持的机会。我们希望您会发现 Spring Boot 3.1 使 SSL 配置变得更加容易。
Spring Boot 3.1 引入了 SSL 包的概念,用于配置和使用自定义 SSL 信任材料,例如密钥库、证书和私钥。配置后,可以使用配置属性或 API 将包应用于一个或多个连接。
用于配置 SSL 信任材料的属性位于application.yaml
或application.properties
文件中的spring.ssl.bundle
前缀下。提供两个顶级分组以反映配置不同类型信任材料所需的唯一信息。
spring.ssl.bundle.jks
可用于使用 Java 密钥库文件配置包。spring.ssl.bundle.pem
可用于使用 PEM 编码的文本文件配置包。可以配置每种类型的一个或多个包,每个配置的包都由用户提供名称。在使用属性应用包或使用 API 检索包时,将使用此名称。
以下application.yaml
文件示例显示了两个 SSL 包的配置。第一个名为server
,并定义了一个 Java 密钥库文件(PKCS #12 格式),可用于保护嵌入式 Web 服务器。第二个名为client
,并定义了一个带有 PEM 编码证书文件的信任库,可用于保护与需要客户端身份验证的服务器的连接的客户端。
spring:
ssl:
bundle:
jks:
server:
key:
alias: "server"
keystore:
location: "classpath:server.p12"
password: "secret"
type: "PKCS12"
pem:
client:
truststore:
certificate: "classpath:client.crt"
请参阅Spring Boot 参考文档以及JksSslBundleProperties 和PemSslBundleProperties 类,以了解有关可用配置属性的更多详细信息。
Spring Boot 使用spring.ssl.bundle
属性创建提供对指定信任材料访问的对象。
如上所述,Java 安全和 SSL API 提供了三个抽象级别来公开从 Java 密钥库或 PEM 文件读取的信任材料。
java.security.KeyStore
实例。javax.net.ssl.KeyManager
和javax.net.ssl.TrustManager
实例。javax.net.ssl.SSLContext
实例。在最低级别,您可能需要信任库和密钥库对象才能将 SSL 应用于连接。可以使用此处所示的SslStoreBundle
接口访问这些对象。
public interface SslStoreBundle {
KeyStore getKeyStore();
String getKeyStorePassword();
KeyStore getTrustStore();
}
可以从密钥库和信任库派生KeyManager
和TrustManager
实例。可以使用SslManagerBundle
接口访问这些实例。
public interface SslManagerBundle {
KeyManager[] getKeyManagers();
KeyManagerFactory getKeyManagerFactory();
TrustManager[] getTrustManagers();
TrustManagerFactory getTrustManagerFactory();
}
最后,可以从KeyManager
和TrustManager
创建SSLContext
,并使用createSslContext
工厂方法访问它。
将所有这些放在一起,我们得到了一个SslBundle
接口,它可以访问各种不同的配置样式。
public interface SslBundle {
SslStoreBundle getStores();
SslManagerBundle getManagers();
SSLContext createSslContext() {
}
请参阅源代码,了解SslBundle
中所有方法的完整列表。
配置的SslBundle
集合在一个SslBundles
bean 中可用,该 bean 可以自动连接到其他 Spring bean。
public interface SslBundles {
SslBundle getBundle(String bundleName) throws NoSuchSslBundleException;
}
使用SslBundles
检索和应用SSLContext
的示例可能如下所示。
@Component
public class MyComponent {
public MyComponent(SslBundles sslBundles) {
SslBundle sslBundle = sslBundles.getBundle("client");
SSLContext sslContext = sslBundle.createSslContext();
// do something with the created sslContext
}
}
Spring Boot 3.1 中启用的 SSL 功能的一个令人兴奋的新领域是 REST 客户端的配置。Spring Boot 对自定义RestTemplate
或WebClient
的支持现在包括将 SSL 包应用于保护客户端和 REST 服务之间的连接的能力。
RestTemplateBuilder
有一个新的setSslBundle()
方法,它接受从自动配置的SslBundles
检索的 SSL 包,如本例所示。
@Service
public class MyService {
private final RestTemplate restTemplate;
public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
this.restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build();
}
}
WebClientSsl
接口允许检索 SSL 包并将其应用于WebClient.Builder
,如本例所示。
@Service
public class MyService {
private final WebClient webClient;
public MyService(WebClient.Builder webClientBuilder, WebClientSsl ssl) {
this.webClient = webClientBuilder.baseUrl("https://example.org").apply(ssl.fromBundle("mybundle")).build();
}
}
Spring Boot 使配置用于从应用程序连接到数据服务的客户端库变得容易。这些客户端库是使用所有三个级别的 Java 安全和 SSL API 进行配置的 API 的良好示例。
在 3.1 版本之前,Spring Boot 为许多提供自动配置的数据服务提供某种形式的 SSL 配置。但是,不同服务间的支持级别和配置属性并不一致。现在大多数数据服务的自动配置属性都具有类似的 ssl
结构,从而在不同服务之间提供了更高的一致性。
spring.cassandra.ssl
spring.couchbase.env.ssl
spring.elasticsearch.restclient.ssl
spring.data.mongodb.ssl
spring.data.redis.ssl
大多数服务都有一个 *.ssl.enabled
属性,它将使用 Java 运行时 cacerts
中包含的可信材料启用客户端库中的 SSL 支持。*.ssl.bundle
属性应用一个命名的 SSL 捆绑包,以使用来自捆绑包的自定义可信材料启用客户端库的 SSL 支持。这使得配置更加一致,并允许将相同的可信材料应用于多个连接,从而减少属性或 YAML 配置的数量。
为了提供这种一致性级别,一些以前的 SSL 相关属性已被弃用。有关更多详细信息,请参阅配置属性变更日志。
JDBC 连接显然不在此列表中。我们计划在即将发布的 Spring Boot 版本中将 SSL 捆绑包方法应用于 JDBC 连接。
Spring Boot 支持的所有嵌入式 Web 服务器都可以通过使用 server.ssl.*
属性配置为使用 SSL 保护传入连接。自 Spring Boot 推出以来就一直支持 Java 密钥库文件,自 2.7 版本以来一直支持 PEM 编码文件。
随着时间的推移,server.ssl
前缀下的属性数量不断增加,并且缺乏结构使得难以判断哪些属性可以一起使用,哪些属性是互斥的。之前的 server.ssl.*
属性仍然受支持,但是可以使用新的 server.ssl.bundle
属性将配置的 SSL 捆绑包应用于嵌入式 Web 服务器。
以下两个示例在功能上是相同的
server:
ssl:
key-alias: “server”
key-password: “keysecret”
key-store: "classpath:server.p12"
key-store-password: "storesecret"
client-auth: NEED
spring:
ssl:
bundle:
jks:
web-server:
key:
alias: "server"
password: “keysecret”
keystore:
location: "classpath:server.p12"
password: "storesecret"
server:
ssl:
bundle: “web-server”
client-auth: NEED
旧的结构更简洁,但新的结构减少了错误配置的可能性,并允许在多个连接上使用相同的 SSL 捆绑包。
对 management.server.ssl
和 spring.rsocket.server.ssl
属性也进行了类似的更改。
我们真的希望您发现 SSL 捆绑包是 Spring Boot 3.1 的一个有用功能。如果您发现我们应该为其添加 SSL 支持的其他技术,请提出GitHub issue,我们会考虑将其纳入未来的版本。