Apache HttpClient with SSL – 带SSL的Apache HttpClient

最后修改: 2013年 8月 9日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

This article will show how to configure the Apache HttpClient 4 with “Accept All” SSL support. The goal is simple – consume HTTPS URLs which do not have valid certificates.

这篇文章将展示如何配置Apache HttpClient 4的 “Accept All “SSL支持。目标很简单–消费没有有效证书的HTTPS URLs。

If you want to dig deeper and learn other cool things you can do with the HttpClient – head on over to the main HttpClient guide.

如果你想更深入地了解你可以用HttpClient做的其他很酷的事情–请到主要HttpClient指南

2. The SSLPeerUnverifiedException

2、SSLPeerUnverifiedException

Without configuring SSL with the HttpClient, the following test – consuming an HTTPS URL – will fail:

如果没有用HttpClient配置SSL,下面的测试–消耗一个HTTPS URL–将会失败。

public class RestClientLiveManualTest {

    @Test(expected = SSLPeerUnverifiedException.class)
    public void whenHttpsUrlIsConsumed_thenException() 
      throws ClientProtocolException, IOException {
 
        CloseableHttpClient httpClient = HttpClients.createDefault();
        String urlOverHttps
          = "https://localhost:8082/httpclient-simple";
        HttpGet getMethod = new HttpGet(urlOverHttps);
        
        HttpResponse response = httpClient.execute(getMethod);
        assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
    }
}

The exact failure is:

确切的失败是。

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)
    ...

The javax.net.ssl.SSLPeerUnverifiedException exception occurs whenever a valid chain of trust couldn’t be established for the URL.

javax.net.ssl.SSLPeerUnverifiedException 异常在无法为 URL 建立有效的信任链时发生。

3. Configure SSL – Accept All (HttpClient < 4.3)

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

Let’s now configure the HTTP client to trust all certificate chains regardless of their validity:

现在让我们把HTTP客户端配置为信任所有的证书链,不管它们是否有效。

@Test
public final void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenOk() 
  throws GeneralSecurityException {
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    CloseableHttpClient httpClient = (CloseableHttpClient) requestFactory.getHttpClient();

    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
    SSLSocketFactory sf = new SSLSocketFactory(acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
    httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 8443, sf));

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

With the new TrustStrategy now overriding the standard certificate verification process (which should consult a configured trust manager) – the test now passes and the client is able to consume the HTTPS URL.

随着新的TrustStrategy现在覆盖了标准的证书验证过程(应该咨询配置的信任管理器)–测试现在通过了,客户端能够消费HTTPS URL

4. Configure SSL – Accept All (HttpClient 4.4 and Above)

4.配置SSL-接受所有(HttpClient 4.4及以上版本)

With the new HTTPClient, now we have an enhanced, redesigned default SSL hostname verifier. Also with the introduction of SSLConnectionSocketFactory and RegistryBuilder, it’s easy to build SSLSocketFactory. So we can write the above test case like :

通过新的HTTPClient,现在我们有一个增强的、重新设计的默认SSL主机名验证器。同时,随着SSLConnectionSocketFactoryRegistryBuilder的引入,构建SSLSocketFactory也很容易。因此,我们可以把上述测试案例写成:

@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. The Spring RestTemplate with SSL (HttpClient < 4.3)

5.带有SSL的Spring RestTemplate(HttpClient < 4.3)

Now that we have seen how to configure a raw HttpClient with SSL support, let’s take a look at a higher level client – the Spring RestTemplate.

现在我们已经看到了如何配置一个支持SSL的原始HttpClient,让我们看看一个更高级别的客户端–Spring RestTemplate

With no SSL configured, the following test fails as expected:

在没有配置SSL的情况下,以下测试如期失败。

@Test(expected = ResourceAccessException.class)
public void whenHttpsUrlIsConsumed_thenException() {
    String urlOverHttps 
      = "https://localhost:8443/httpclient-simple/api/bars/1";
    ResponseEntity<String> response 
      = new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

So let’s configure SSL:

因此,让我们来配置SSL。

@Test
public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() 
  throws GeneralSecurityException {
    HttpComponentsClientHttpRequestFactory requestFactory 
      = new HttpComponentsClientHttpRequestFactory();
    DefaultHttpClient httpClient
      = (DefaultHttpClient) requestFactory.getHttpClient();
    TrustStrategy acceptingTrustStrategy = (cert, authType) -> true
    SSLSocketFactory sf = new SSLSocketFactory(
      acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER);
    httpClient.getConnectionManager().getSchemeRegistry()
      .register(new Scheme("https", 8443, sf));

    String urlOverHttps
      = "https://localhost:8443/httpclient-simple/api/bars/1";
    ResponseEntity<String> response = new RestTemplate(requestFactory).
      exchange(urlOverHttps, HttpMethod.GET, null, String.class);
    assertThat(response.getStatusCode().value(), equalTo(200));
}

As you can see, this is very similar to the way we configured SSL for the raw HttpClient – we configure the request factory with SSL support and then we instantiate the template passing this preconfigured factory.

正如你所看到的,这与我们为原始HttpClient配置SSL的方式非常相似–我们配置支持SSL的请求工厂,然后我们通过这个预配置的工厂实例化模板。

6. The Spring RestTemplate with SSL (HttpClient 4.4)

6.带SSL的Spring RestTemplate(HttpClient 4.4)

And we can use the same way to configure our RestTemplate:

而我们可以用同样的方式来配置我们的RestTemplate

@Test
public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect() 
throws ClientProtocolException, IOException {
    CloseableHttpClient httpClient
      = HttpClients.custom()
        .setSSLHostnameVerifier(new NoopHostnameVerifier())
        .build();
    HttpComponentsClientHttpRequestFactory requestFactory 
      = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);

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

7. Conclusion

7.结论

This tutorial discussed how to configure SSL for an Apache HttpClient so that it is able to consume any HTTPS URL, regardless of the certificate. The same configuration for the Spring RestTemplate is also illustrated.

本教程讨论了如何为Apache HttpClient配置SSL,以便它能够消费任何HTTPS URL,而不管证书如何。还说明了Spring RestTemplate的相同配置。

An important thing to understand however is that this strategy entirely ignores certificate checking – which makes it insecure and only to be used where that makes sense.

然而,需要了解的一个重要问题是,这种策略完全忽略了证书检查–这使得它不安全,只能在有意义的情况下使用。

The implementation of these examples can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

这些例子的实现可以在GitHub 项目中找到 – 这是一个基于 Eclipse 的项目,因此应该很容易导入并按原样运行。