[譯]HttpClient請求HTTPS URL
1.概覽
本文將演示如何配置Apache HttpClient 4 添加ssl支持.目的很簡單----無需有效證書即可成功請求 HTTPS URLs.
如果你想深入挖掘和學習其他和HttpClient相關的酷知識,請點擊httpclient-guide
延伸閱讀:
httpclient-connection-management
httpclient-advanced-config
httpclient-4-cookies
2. SSLPeerUnverifiedException異常
使用httpclient若未配置SSL,下面的測試----請求一個HTTPS URL----將會失敗:
1public 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 urlOverHttps9 = "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.原文地址:
傳送門
[譯]HttpClient請求HTTPS URL