使用 SSL 保护 Spring Boot 应用

工程 | Scott Frederick | 2023年6月7日 | ...

安全套接字层 (SSL) 和传输层安全 (TLS) 是在分层或面向服务的架构中保护系统间通信的关键组成部分。在这种架构中的 Spring Boot 应用经常接受传入的网络连接或创建传出的连接,开发者需要负责配置应用以在这种安全环境中工作。

如果你曾使用过 Java 安全和 SSL API,你可能会意识到这不是一项特别有趣的任务。它通常需要多次访问 stackoverflow.com 来复制粘贴代码。有几个因素使得使用 SSL 变得困难。

首先,你可能会获得用于生产环境的信任材料,例如证书和私钥。你可能需要为预生产测试生成不同的信任材料(通常使用自签名证书颁发机构)。这些信任材料通常是 JKS 或 PKCS #12 格式的 Java 密钥库文件,或者它们可能是 PEM 编码的文本文件。每种文件类型都需要不同的处理方式。

一旦你获得了信任材料,你需要将其转换为可以传递给 Java 连接 API 的形式。这可能会变得很困难,因为连接 API 可以通过多种方式进行配置

  • 有些需要你提供密钥库和信任库的 java.security.KeyStore 实例。
  • 有些需要你提供 javax.net.ssl.KeyManagerjavax.net.ssl.TrustManager 实例。
  • 有些需要你提供一个 javax.net.ssl.SSLContext 实例。

SSL 的抽象级别也相当低,所以你通常需要剥开几层抽象才能找到需要使用 java.securityjava.net.ssl 包中的对象进行配置的部分。例如,如果你想在 Spring 的 RestTemplate 上配置 SSL,你需要深入到底层支持它的 ClientHttpRequestFactory。对于典型的 Spring Boot 应用,这可能是 HttpComponentsClientHttpRequestFactoryOkHttp3ClientHttpRequestFactorySimpleClientHttpRequestFactory。这些类都提供了不同的配置 API。

配置连接使用 SSL 或 TLS 对 Spring Boot 来说并非新鲜事,但团队决定对当前支持的内容进行整体审视,并寻找改进和扩展支持的机会。我们希望你会发现 Spring Boot 3.1 让 SSL 配置变得更加容易。

介绍 SSL Bundle

Spring Boot 3.1 引入了 SSL Bundle 的概念,用于配置和使用自定义的 SSL 信任材料,例如密钥库、证书和私钥。一旦配置完成,一个 bundle 可以通过配置属性或 API 应用于一个或多个连接。

配置 SSL Bundle

用于配置 SSL 信任材料的属性位于 application.yamlapplication.properties 文件中的 spring.ssl.bundle 前缀下。提供了两个顶层分组,以反映配置不同类型信任材料所需的独特信息。

  • spring.ssl.bundle.jks 可用于使用 Java 密钥库文件配置 bundle。
  • spring.ssl.bundle.pem 可用于使用 PEM 编码的文本文件配置 bundle。

可以配置每种类型的一个或多个 bundle,每个已配置的 bundle 都会被指定一个用户提供的名称。该名称用于通过属性应用 bundle 或通过 API 获取 bundle。

以下的示例 application.yaml 文件展示了两个 SSL bundle 的配置。第一个名为 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 参考文档以及 JksSslBundlePropertiesPemSslBundleProperties 类。

使用自动配置的 SSL Bundle

Spring Boot 使用 spring.ssl.bundle 属性来创建对象,这些对象提供对指定信任材料的访问。

如上所述,Java 安全和 SSL API 提供了三个抽象级别来暴露从 Java 密钥库或 PEM 文件读取的信任材料

  • 用作密钥库和信任库的 java.security.KeyStore 实例。
  • javax.net.ssl.KeyManagerjavax.net.ssl.TrustManager 实例。
  • javax.net.ssl.SSLContext 实例。

在最低级别,你可能需要信任库和密钥库对象来将 SSL 应用于连接。可以使用 SslStoreBundle 接口访问这些对象,如下所示

public interface SslStoreBundle {

	KeyStore getKeyStore();

	String getKeyStorePassword();

	KeyStore getTrustStore();

}

KeyManagerTrustManager 实例可以从密钥库和信任库派生。可以使用 SslManagerBundle 接口访问这些对象

public interface SslManagerBundle {

	KeyManager[] getKeyManagers();

	KeyManagerFactory getKeyManagerFactory();

	TrustManager[] getTrustManagers();

	TrustManagerFactory getTrustManagerFactory();

}

最后,可以从 KeyManagerTrustManager 创建一个 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
    }

}

保护 REST 客户端

Spring Boot 3.1 中新增的一个令人兴奋的 SSL 功能是 REST 客户端的配置。Spring Boot 对定制 RestTemplateWebClient 的支持现在包含了应用 SSL bundle 来保护客户端和 REST 服务之间连接的能力。

RestTemplateBuilder 有一个新的 setSslBundle() 方法,它接受从自动配置的 SslBundles 中获取的 SSL bundle,示例如下

@Service
public class MyService {

    private final RestTemplate restTemplate;

    public MyService(RestTemplateBuilder restTemplateBuilder, SslBundles sslBundles) {
        this.restTemplate = restTemplateBuilder.setSslBundle(sslBundles.getBundle("mybundle")).build();
    }

}

WebClientSsl 接口允许获取 SSL bundle 并将其应用于 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 三个级别中任何一个进行配置的良好示例。

在 3.1 版本之前,Spring Boot 为许多数据服务提供了某种形式的 SSL 配置的自动配置。然而,支持级别和用于配置的属性在不同服务之间存在不一致。现在,大多数数据服务自动配置属性都具有类似的 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 属性,该属性将使用 Java 运行时 cacerts 中包含的信任材料启用客户端库中的 SSL 支持。*.ssl.bundle 属性应用一个命名 SSL bundle,以使用 bundle 中的自定义信任材料启用客户端库 SSL 支持。这使得配置更加一致,并允许将相同的信任材料应用于多个连接,从而减少了属性或 YAML 配置的数量。

为了提供这种一致性,一些之前的 SSL 相关属性已被弃用。有关更多详细信息,请参阅配置属性变更日志

JDBC 连接显然在此列表中有所遗漏。我们计划在未来的 Spring Boot 版本中将 SSL bundle 方法应用于 JDBC 连接。

保护嵌入式 Web 服务器

Spring Boot 支持的所有嵌入式 Web 服务器都可以使用 server.ssl.* 属性配置为使用 SSL 保护传入连接。自 Spring Boot 问世以来就支持 Java 密钥库文件,而 PEM 编码的文件自 2.7 版本起就已支持。

server.ssl 前缀下的属性数量随着时间的推移不断增加,缺乏结构使得难以区分哪些属性可以一起使用以及哪些属性是互斥的。以前的 server.ssl.* 属性继续受支持,但一个新的 server.ssl.bundle 属性可用于将配置的 SSL bundle 应用于嵌入式 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 bundle。

management.server.sslspring.rsocket.server.ssl 属性也进行了类似的更改。

未来工作

我们真诚希望你发现 SSL bundle 是 Spring Boot 3.1 的一个有用功能。如果你发现任何其他你认为我们应该为其添加 SSL 支持的技术,请在 GitHub 上提交 issue,我们将在未来的版本中考虑。

获取 Spring 快讯

订阅 Spring 快讯,保持联系

订阅

抢先一步

VMware 提供培训和认证,助你快速提升。

了解更多

获取支持

Tanzu Spring 通过一项简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部