使用 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 捆綁包

Spring Boot 3.1 引入了 SSL 捆綁包的概念,用於配置和使用自定義 SSL 信任材料,如金鑰庫、證書和私鑰。配置後,捆綁包可以使用配置屬性或 API 應用到一個或多個連線。

配置 SSL 捆綁包

用於配置 SSL 信任材料的屬性位於 application.yamlapplication.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 參考文件以及 JksSslBundlePropertiesPemSslBundleProperties 類。

使用自動配置的 SSL 捆綁包

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 中可用,可以自動裝配到其他 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 捆綁包以保護客戶端和 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 的三個配置級別的良好示例。

在 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 捆綁包,以使用捆綁包中的自定義信任材料啟用客戶端庫 SSL 支援。這使得配置更加一致,並允許將相同的信任材料應用於多個連線,從而減少了屬性或 YAML 配置的數量。

為了提供這種程度的一致性,一些以前與 SSL 相關的屬性已被棄用。有關更多詳細資訊,請參閱配置屬性變更日誌

JDBC 連線顯然被遺漏了。我們計劃在即將釋出的 Spring Boot 版本中將 SSL 捆綁包方法應用於 JDBC 連線。

保護嵌入式 Web 伺服器

Spring Boot 支援的所有嵌入式 Web 伺服器都可以透過使用 server.ssl.* 屬性配置 SSL 來保護傳入連線。Java 金鑰庫檔案自 Spring Boot 誕生以來一直受支援,而 PEM 編碼檔案自 2.7 版本以來一直受支援。

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.sslspring.rsocket.server.ssl 屬性也進行了類似更改。

未來工作

我們真的希望您發現 SSL 捆綁包是 Spring Boot 3.1 的一個有用功能。如果您發現任何其他您認為我們應該為其新增 SSL 支援的技術,請提出 GitHub 問題,我們將在未來的版本中考慮它。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有