1. 引言
在 Spring Boot 應用程序中管理安全的通信通常涉及處理複雜的配置。挑戰通常始於處理信任材料,例如證書和私鑰,這些材料以各種格式出現,例如 JKS、PKCS #12 或 PEM。每種格式都有其自身的要求,用於如何處理它們。
幸運的是,Spring Boot 3.1 引入了 SSL 捆綁包,這是一個旨在簡化這些複雜性的功能。在本教程中,我們將探索 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 捆包的配置屬性位於 spring.ssl.bundle 前綴下的 application.yaml 或 application.properties 配置文件中。
讓我們從 JKS 捆包開始。 我們使用 spring.ssl.bundle.jks 來配置使用 Java Keystore 文件的捆包:
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 自動化創建 Java 對象,如 KeyStore、KeyManager 和 SSLContext,基於 SSL 捆包的配置。 這消除了手動創建和管理這些對象的需要,使過程更簡單,更不易出錯。
3. 使用 SSL 捆綁包安全地配置 RestTemplate
讓我們從使用 RestTemplate 豆時,利用 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 豆來調用 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 捆綁包用於驗證 secure-service 的證書,從而確保了加密和安全的通信通道。 但是,這並不能限制我們使用客户端證書進行 API 側的身份驗證。 我們稍後將看到如何獲取 SSLContext 以配置自定義客户端。
4. 利用 Spring Boot 的自動配置的 SSLBundles
在 Spring Boot 的 SSL Bundles 出現之前,開發者需要使用經典的 Java 類來支持 SSL 配置:
- java.security.KeyStore:這些實例用作 keystore 和 truststore,有效地充當加密密鑰和證書的安全存儲庫。
- 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 客户端的 secure communications 非常有用。
例如,讓我們考慮為自定義 secure 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 Bundles 的方式。 因此,我們檢索 secure-service bundle 並創建上下文。 稍後,我們使用 SSLContext 實例來創建一個自定義 HttpClient 並將其應用於創建 RestTemplate bean。
5. 使用 SSL 捆綁包與數據服務
不同的數據服務具有不同的 SSL 配置選項,這在配置過程中會帶來複雜性。
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 屬性。此屬性在客户端庫中啓用 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
另一方面,SSL Bundle方法允許將相同的配置封裝在一起:
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 Bundle方法對於配置管理更有效。此功能也適用於其他配置,如management.server.ssl 和 spring.rsocket.server.ssl。
7. 結論
在本文中,我們探討了 Spring Boot 中的新 SSL Bundles 功能,該功能可以簡化並統一配置信任材料的過程。
與傳統的 server.ssl.* 屬性相比,SSL Bundles 提供了一種結構化的方式來管理 keystore 和 truststore。這在降低配置錯誤風險和提高跨多服務 SSL 管理效率方面尤其有益。
此外,SSL Bundles 適用於集中管理,允許相同的 bundle 在應用程序的不同部分重複使用。
通過採用 SSL bundles,開發人員不僅可以簡化配置過程,還可以提升 Spring Boot 應用程序嵌入式 Web 服務器的安全性態勢。