1.概览
本文将演示如何配置Apache HttpClient 4 添加ssl支持.目的很简单----无需有效证书即可成功请求 HTTPS URLs.
如果你想深入挖掘和学习其他和HttpClient相关的酷知识,请点击httpclient-guide
延伸阅读:
httpclient-connection-management
2. SSLPeerUnverifiedException异常
使用httpclient若未配置SSL,下面的测试----请求一个HTTPS URL----将会失败:
1 public class HttpLiveTest { 2 3 @Test(expected = SSLPeerUnverifiedException.class) 4 public void whenHttpsUrlIsConsumed_thenException() 5 throws ClientProtocolException, IOException { 6 7 DefaultHttpClient httpClient = new DefaultHttpClient(); 8 String urlOverHttps 9 = "https://localhost:8080/spring-security-rest-basic-auth"; 10 HttpGet getMethod = new HttpGet(urlOverHttps); 11 12 HttpResponse response = httpClient.execute(getMethod); 13 assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); 14 } 15 }
具体的异常是:
1 javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated 2 at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397) 3 at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126) 4 ...
不管何时,URL不能建立一个信任的有效链时,都会出现javax.net.ssl.SSLPeerUnverifiedException exception异常.
3.配置SSL--Accept All(HttpClient的版本小于4.3)
下面通过配置HTTP client信任所有链(译者注:chains)无论他们是否有效.
1 @Test 2 public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() 3 throws IOException, GeneralSecurityException { 4 TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; 5 SSLSocketFactory sf = new SSLSocketFactory( 6 acceptingTrustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 7 SchemeRegistry registry = new SchemeRegistry(); 8 registry.register(new Scheme("https", 8443, sf)); 9 ClientConnectionManager ccm = new PoolingClientConnectionManager(registry); 10 11 DefaultHttpClient httpClient = new DefaultHttpClient(ccm); 12 13 String urlOverHttps 14 = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; 15 HttpGet getMethod = new HttpGet(urlOverHttps); 16 17 HttpResponse response = httpClient.execute(getMethod); 18 assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); 19 }
在新的信任策略下,覆盖了原有的标准证书验证过程(原本需要咨询一个配置好的信任管理器)----上面的测试通过则表明现在的client可以请求HTTPS URL了.
4.spring的RestTemplate配置SSL(HttpClient的版本小于4.3)
我们已经知晓如何给原生的HttpClient配置添加SSL支持,再看看一下更高级的client----the Spring RestTemplate.
没有配置SSL的情况下,如预期一致,下面的测试将不会通过:
1 @Test(expected = ResourceAccessException.class) 2 public void whenHttpsUrlIsConsumed_thenException() { 3 String urlOverHttps 4 = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; 5 ResponseEntity<String> response 6 = new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null, String.class); 7 assertThat(response.getStatusCode().value(), equalTo(200)); 8 }
下面配置SSL:
1 import static org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; 2 import java.security.GeneralSecurityException; 3 import java.security.cert.X509Certificate; 4 import org.apache.http.auth.AuthScope; 5 import org.apache.http.auth.UsernamePasswordCredentials; 6 import org.apache.http.conn.scheme.Scheme; 7 import org.apache.http.conn.ssl.SSLSocketFactory; 8 import org.apache.http.conn.ssl.TrustStrategy; 9 import org.apache.http.impl.client.DefaultHttpClient; 10 import org.springframework.http.HttpMethod; 11 import org.springframework.http.ResponseEntity; 12 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 13 import org.springframework.web.client.ResourceAccessException; 14 import org.springframework.web.client.RestTemplate; 15 16 ... 17 @Test 18 public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() 19 throws GeneralSecurityException { 20 HttpComponentsClientHttpRequestFactory requestFactory 21 = new HttpComponentsClientHttpRequestFactory(); 22 DefaultHttpClient httpClient 23 = (DefaultHttpClient) requestFactory.getHttpClient(); 24 TrustStrategy acceptingTrustStrategy = (cert, authType) -> true 25 SSLSocketFactory sf = new SSLSocketFactory( 26 acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER); 27 httpClient.getConnectionManager().getSchemeRegistry() 28 .register(new Scheme("https", 8443, sf)); 29 30 String urlOverHttps 31 = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; 32 ResponseEntity<String> response = new RestTemplate(requestFactory). 33 exchange(urlOverHttps, HttpMethod.GET, null, String.class); 34 assertThat(response.getStatusCode().value(), equalTo(200)); 35 }
正如你所见,这和原生的HttpClient配置SSL非常相像----我们给 request factory 添加了SSL支持,然后初始化模板时将配置好的factory作为入参.
5.配置SSL(HttpClient版本为4.4)
在4.4版本,不再使用SSLSocketFactory,可简单配置如下:
1 @Test 2 public void givenIgnoringCertificates_whenHttpsUrlIsConsumed_thenCorrect() 3 throws Exception { 4 SSLContext sslContext = new SSLContextBuilder() 5 .loadTrustMaterial(null, (certificate, authType) -> true).build(); 6 7 CloseableHttpClient client = HttpClients.custom() 8 .setSSLContext(sslContext) 9 .setSSLHostnameVerifier(new NoopHostnameVerifier()) 10 .build(); 11 HttpGet httpGet = new HttpGet(HOST_WITH_SSL); 12 httpGet.setHeader("Accept", "application/xml"); 13 14 HttpResponse response = client.execute(httpGet); 15 assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); 16 }
6.Spring RestTemplate 配置 SSL (HttpClient 4.4)
我们可以使用同样的方式配置RestTemplate :
1 @Test 2 public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect() 3 throws ClientProtocolException, IOException { 4 CloseableHttpClient httpClient 5 = HttpClients.custom() 6 .setSSLHostnameVerifier(new NoopHostnameVerifier()) 7 .build(); 8 HttpComponentsClientHttpRequestFactory requestFactory 9 = new HttpComponentsClientHttpRequestFactory(); 10 requestFactory.setHttpClient(httpClient); 11 12 ResponseEntity<String> response 13 = new RestTemplate(requestFactory).exchange( 14 urlOverHttps, HttpMethod.GET, null, String.class); 15 assertThat(response.getStatusCode().value(), equalTo(200)); 16 }
7.总结
本教程讨论了如何给 Apache HttpClient 配置SSL ,忽略校验以至于能够访问任何 HTTPS URL .并举例说明给Spring RestTemplate配置SSL.
然而需要明白的是:该策略完全忽略证书验证,这可能导致安全漏洞,因此只能用在需要的地方.
本文的示例代码可访问 the GitHub project ,工程基于Eclipse,因此可以轻松导入并运行.
8.原文地址: