本文将展示如何使用“接受所有”SSL支持来配置Apache HttpClient 4。目标很简单 - 使用没有有效证书的HTTPS URL。
在没有使用HttpClient配置SSL的情况下,以下测试(使用HTTPS URL)将失败:
public class HttpLiveTest {
@Test(expected = SSLPeerUnverifiedException.class)
public void whenHttpsUrlIsConsumed_thenException()
throws ClientProtocolException, IOException {
DefaultHttpClient httpClient = new DefaultHttpClient();
String urlOverHttps
= "https://localhost:8080/spring-security-rest-basic-auth";
HttpGet getMethod = new HttpGet(urlOverHttps);
HttpResponse response = httpClient.execute(getMethod);
assertThat(response.getStatusLine().getStatusCode(), 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)
...
该javax.net.ssl.SSLPeerUnverifiedException例外,只要有效信任链无法为URL建立发生。
现在让我们将HTTP客户端配置为信任所有证书链,而不管它们的有效性如何:
@Test
public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException()
throws IOException, GeneralSecurityException {
TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
SSLSocketFactory sf = new SSLSocketFactory(
acceptingTrustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", 8443, sf));
ClientConnectionManager ccm = new PoolingClientConnectionManager(registry);
DefaultHttpClient httpClient = new DefaultHttpClient(ccm);
String urlOverHttps
= "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1";
HttpGet getMethod = new HttpGet(urlOverHttps);
HttpResponse response = httpClient.execute(getMethod);
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
}
随着新的TrustStrategy现在覆盖标准证书验证过程(应该咨询配置的信任管理器) - 测试现在通过,客户端可以使用HTTPS URL。
现在我们已经看到了如何配置一个支持SSL 的原始HttpClient,让我们来看看更高级别的客户端--Spring RestTemplate。
未配置SSL,以下测试将按预期失败:
@Test(expected = ResourceAccessException.class)
public void whenHttpsUrlIsConsumed_thenException() {
String urlOverHttps
= "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1";
ResponseEntity<String> response
= new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode().value(), equalTo(200));
}
那么让我们来配置SSL:
import static org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.DefaultHttpClient;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;
...
@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/spring-security-rest-basic-auth/api/bars/1";
ResponseEntity<String> response = new RestTemplate(requestFactory).
exchange(urlOverHttps, HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode().value(), equalTo(200));
}
如您所见,这与我们为原始HttpClient配置SSL的方式非常相似 - 我们使用SSL支持配置请求工厂,然后实例化通过此预配置工厂的模板。
在HttpClient 4.4版本中,现在不推荐使用SSLSocketFactory,我们可以简单地配置我们的HttpClient,如下所示:
@Test
public void givenIgnoringCertificates_whenHttpsUrlIsConsumed_thenCorrect()
throws Exception {
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(null, (certificate, authType) -> true).build();
CloseableHttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpGet httpGet = new HttpGet(HOST_WITH_SSL);
httpGet.setHeader("Accept", "application/xml");
HttpResponse response = client.execute(httpGet);
assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
}
我们可以用同样的方法来配置我们的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));
}
本教程讨论了如何为Apache HttpClient配置SSL,以便它能够使用任何HTTPS URL,而不考虑证书。还展示了Spring RestTemplate的相同配置。
然而,要理解的重要一点是,这种策略完全忽略了证书检查 - 这使得它不安全,只能在有意义的地方使用。
https://www.leftso.com/article/396.html