知識庫 / HTTP Client-Side RSS 訂閱

Apache HttpClient 使用 SSL/TLS 支持

HTTP Client-Side,Spring Web
HongKong
11
03:02 PM · Dec 06 ,2025

1. 概述

本文將演示如何配置 Apache HttpClient 4 & 5,並啓用“接受所有” SSL 支持。目標很簡單——消費無效證書的 HTTPS URL。

如果您想深入瞭解 HttpClient 的更多高級功能,請參閲 主 HttpClient 指南

2. SSLPeerUnverifiedException

在沒有配置 HttpClient 的 SSL 協議的情況下,以下測試 – 消費 HTTPS URL – 將失敗:

@Test
void whenHttpsUrlIsConsumed_thenException() {
    String urlOverHttps = "https://localhost:8082/httpclient-simple";
    HttpGet getMethod = new HttpGet(urlOverHttps);

    assertThrows(SSLPeerUnverifiedException.class, () -> {
        CloseableHttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = httpClient.execute(getMethod, new CustomHttpClientResponseHandler());
        assertThat(response.getCode(), equalTo(200));
    });
}

確切的故障信息如下:

javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
    at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126)
    ...

當無法為 URL 建立有效的信任鏈時,會發生 javax.net.ssl.SSLPeerUnverifiedException 異常。

3. 配置 SSL – 接受所有 (HttpClient 5)

現在,我們將配置 HTTP 客户端,使其無論證書鏈的有效性如何,都信任所有證書鏈:

@Test
void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() throws GeneralSecurityException, IOException {

    final HttpGet getMethod = new HttpGet(HOST_WITH_SSL);

    final TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    final SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial(null, acceptingTrustStrategy)
        .build();
    final SSLConnectionSocketFactory sslsf = 
        new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    final Registry<ConnectionSocketFactory> socketFactoryRegistry = 
        RegistryBuilder.<ConnectionSocketFactory> create()
        .register("https", sslsf)
        .register("http", new PlainConnectionSocketFactory())
        .build();

    final BasicHttpClientConnectionManager connectionManager =
        new BasicHttpClientConnectionManager(socketFactoryRegistry);

    try( CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(connectionManager)
        .build();

        CloseableHttpResponse response = (CloseableHttpResponse) httpClient
            .execute(getMethod, new CustomHttpClientResponseHandler())) {

            final int statusCode = response.getCode();
            assertThat(statusCode, equalTo(HttpStatus.SC_OK));
    }
}

憑藉新引入的 TrustStrategy 機制,現在 覆蓋了標準證書驗證流程(該流程應諮詢配置好的信任管理器)—— 測試現在通過,並且 客户端能夠消費 HTTPS URL

4. 配置 SSL – 接受所有 (HttpClient 4.5)

@Test
public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk()
  throws GeneralSecurityException {
    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
    SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, 
      NoopHostnameVerifier.INSTANCE);
    
    Registry<ConnectionSocketFactory> socketFactoryRegistry = 
      RegistryBuilder.<ConnectionSocketFactory> create()
      .register("https", sslsf)
      .register("http", new PlainConnectionSocketFactory())
      .build();

    BasicHttpClientConnectionManager connectionManager = 
      new BasicHttpClientConnectionManager(socketFactoryRegistry);
    CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
      .setConnectionManager(connectionManager).build();

    HttpComponentsClientHttpRequestFactory requestFactory = 
      new HttpComponentsClientHttpRequestFactory(httpClient);
    ResponseEntity<String> response = new RestTemplate(requestFactory)
      .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

5. 使用 SSL 的 Spring RestTemplate (HttpClient 5)

現在我們已經瞭解瞭如何配置帶 SSL 支持的原始 HttpClient,現在讓我們來查看一個更高層次的客户端——Spring RestTemplate

在未配置 SSL 的情況下,以下測試將按預期失敗:

@Test
void whenHttpsUrlIsConsumed_thenException() {
    final String urlOverHttps = "https://localhost:8443/httpclient-simple/api/bars/1";

    assertThrows(ResourceAccessException.class, () -> {
        final ResponseEntity<String> response = new RestTemplate()
            .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
        assertThat(response.getStatusCode().value(), equalTo(200));
    });
}

讓我們配置SSL:

@Test
void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() throws GeneralSecurityException {

    final TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    final SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial(null, acceptingTrustStrategy)
        .build();
    final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    final Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
        .register("https", sslsf)
        .register("http", new PlainConnectionSocketFactory())
        .build();

    final BasicHttpClientConnectionManager connectionManager = 
        new BasicHttpClientConnectionManager(socketFactoryRegistry);
    final CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(connectionManager)
        .build();

    final HttpComponentsClientHttpRequestFactory requestFactory =
        new HttpComponentsClientHttpRequestFactory(httpClient);
    final ResponseEntity<String> response = new RestTemplate(requestFactory)
        .exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode()
        .value(), equalTo(200));
}

正如您所見,這與我們為原始 HttpClient 配置 SSL 的方式非常相似——我們配置請求工廠以支持 SSL,然後實例化模板,並將此預配置的工廠傳遞給它。

2. Spring RestTemplate 與 SSL (HttpClient 4.5)

@Test
void givenAcceptingAllCertificates_whenUsingRestTemplate_thenCorrect() {
    final CloseableHttpClient httpClient = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    final HttpComponentsClientHttpRequestFactory requestFactory
        = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

    final ResponseEntity<String> response = new RestTemplate(requestFactory).exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

7. 結論本教程討論瞭如何為 Apache HttpClient 配置 SSL,使其能夠消費任何 HTTPS URL,無論證書如何。同樣,也演示了 Spring RestTemplate 的配置方法。

然而,需要注意的是 這種策略完全忽略了證書驗證——這使其不安全,僅適用於在證書驗證不影響的情況下使用。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.