使用SSL捆绑包保护Spring Boot应用程序

2025/03/22

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服务器的安全状况。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章