在 Java 中透過 Jersey 用戶端使用 HTTPS
1. 簡介
在本快速教學中,我們將探索如何在 Java 中設定 Jersey 用戶端以使用 HTTPS 端點。我們將從使用 JVM 參數進行基本的 SSL 配置開始,逐步講解更高級的場景,包括自訂 SSL 上下文和雙向 TLS 驗證。
2. 場景和設定
為了方便範例,我們將針對需要不同層級驗證的 HTTPS 端點進行測試。為了模擬這種情況,我們使用帶有自訂 SSL 憑證的 WireMock。我們將這些憑證儲存在certs.dir
系統屬性指定的目錄中。接下來,我們將使用Shell 腳本產生必要的證書,並在測試中引用這些證書:
-
server.api.service.p12
:包含伺服器憑證的金鑰庫 -
trust.api.service.p12
:包含受信任憑證的信任庫 -
client.api.service.p12
:包含客戶端雙向 TLS 憑證的客戶端金鑰庫
最後,我們將建立幾個變數來存取我們的系統屬性:
static final String CERTS_DIR = System.getProperty("certs.dir");
static final String PASSWORD = System.getProperty("certs.password");
3. 使用 JVM 參數進行基本 HTTPS 配置
在 Java 中配置 SSL 最簡單的方法是使用 JVM 系統屬性。當我們想要全域設定 SSL 時,這種方法非常方便:
@Test
void whenUsingJVMParameters_thenCorrectCertificateUsed() {
System.setProperty("javax.net.ssl.trustStore", CERTS_DIR + "/trust.api.service.p12");
System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD);
Response response = ClientBuilder.newClient()
.target("https://api.service:8080/test")
.request()
.get();
assertEquals(200, response.getStatus());
}
這種方法有效是因為:
-
javax.net.ssl.trustStore
指定包含受信任憑證的信任函式庫的路徑 - Jersey Client 在建立 SSL 連線時自動使用這些系統屬性
其優點是實作簡單,並且對 JVM 中的所有 SSL 連線都有效。另一方面,其缺點是全域作用域可能會影響應用程式的其他部分,因為這會影響任何請求,而不僅僅是使用 Jersey 用戶端發出的請求。
此外,對於需要不同憑證的不同端點來說,它的靈活性較差。
4. 自訂 SSL 上下文配置
為了更好地控制 SSL 配置,讓我們藉助 Apache HTTP SSLContextBuilder
建立一個自訂上下文,以便在 Jersey 用戶端中使用:
@Test
void whenUsingCustomSSLContext_thenCorrectCertificateUsed() {
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(Paths.get(CERTS_DIR + "/trust.api.service.p12"), PASSWORD)
.build();
// ...
}
首先,我們呼叫loadTrustMaterial()
來載入包含受信任憑證的信任函式庫。然後,我們在 Jersey 的ClientBuilder
中引用 SSL 上下文:
Client client = ClientBuilder.newBuilder()
.sslContext(sslContext)
.build();
最後, sslContext()
方法讓我們明確設定此客戶端的 SSL 上下文,這會影響透過該客戶端發出的所有請求:
Response response = client
.target("https://api.service:8080/test")
.request()
.get();
assertEquals(200, response.getStatus());
主要優點是對 SSL 配置進行細粒度控制,可以針對不同的端點進行不同的配置。
5. 相互 TLS (mTLS) 認證
如果伺服器端啟用了雙向 TLS 驗證,則用戶端和伺服器都必須提供憑證進行驗證。這確保了雙方都經過身份驗證,從而提供了更強的安全性。讓我們看看當我們需要連接的端點需要客戶端身份驗證時,我們的設定是什麼樣的:
@Test
void whenUsingMutualTLS_thenCorrectCertificateUsed() {
char[] password = PASSWORD.toCharArray();
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(Paths.get(CERTS_DIR + "/trust.api.service.p12"), password)
.loadKeyMaterial(Paths.get(CERTS_DIR + "/client.api.service.p12"), password, password)
.build();
Client client = ClientBuilder.newBuilder()
.sslContext(sslContext)
.build();
Response response = client
.target("https://api.service:8080/test")
.request()
.get();
assertEquals(200, response.getStatus());
}
這種方法的唯一區別是設定loadKeyMaterial()
,它會載入客戶端憑證和私鑰以進行客戶端身份驗證。
6. 結論
在本文中,我們探討了使用 Jersey Client 配置 HTTPS 的不同方法,從簡單的 JVM 參數到雙向 TLS,後者透過雙向身分驗證提供最高的安全性。最重要的是,Jersey Client 提供了有效處理這些不同場景的靈活性。
與往常一樣,原始碼可在 GitHub 上取得。