進入正題:
????????我們系統(tǒng)依賴一個外部查詢接口,是HTTPS提供服務的,由于外部系統(tǒng)沒有測試環(huán)境,所以我們都是直接請求的他們生產(chǎn)環(huán)境。項目里面我們使用的HttpClient,其創(chuàng)建SSLClient的代碼如下:
private static CloseableHttpClient createSSLClientDefault() {
SSLContext sslContext;
try {
sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
//信任所有
@Override
public boolean isTrusted(X509Certificate[] xcs, String string) {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (Exception ex) {
logger.error(ex.getMessage(), ex);
}
return HttpClients.createDefault();
}
這樣就可以正常的調用HTTPS服務了。但是當我們的服務部署到線上環(huán)境以后,由于生產(chǎn)機器找不到外部接口的域名,所以配置了host,指向該外部接口所在的Nginx服務器,但是請求一直報錯:doesn't match any of the subject alternative names: []
肥荔。用curl -k
調用該接口,報(35) SSL connect error
錯誤,當初還一度懷疑linux機器openssl版本問題,所以強制升級了一個版本還是沒有解決粟矿。后面知道curl有個-v
的參數(shù),錯誤信息如下:
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* NSS error -5990
* Closing connection #0
* SSL connect error
curl: (35) SSL connect error
所以按照網(wǎng)上的流程升級了一下nss版本,最終curl -k可以調通服務了撩轰。但是通過httpclient還是報錯,所以可以排除掉openssl的問題。所以只能分析下測試環(huán)境和生產(chǎn)環(huán)境的差異,看問題到底出現(xiàn)在什么地方迹辐。
????????后面通過咨詢外部接口的同事,了解到他們的外部接口服務接入了公司內部的智能網(wǎng)關,在智能網(wǎng)關配置了統(tǒng)一的HTTPS證書。而且只要接入了智能網(wǎng)關的服務,請求都會先經(jīng)過智能網(wǎng)關,然后轉發(fā)到他們自己的服務器甚侣。但是生產(chǎn)環(huán)境和智能網(wǎng)關不在同一網(wǎng)絡,而且解析不到外部接口的域名,只能通過host映射到他們的Nginx服務器上明吩。看一下測試環(huán)境請求外部接口的網(wǎng)絡拓撲:
生產(chǎn)環(huán)境的請求網(wǎng)絡拓撲:
????????總算有點眉目了,測試環(huán)境能調通主要是能在內部DNS服務器解析到對應域名殷费。而生產(chǎn)環(huán)境是通過host來做的映射,當去解析域名的時候發(fā)現(xiàn)當前網(wǎng)絡沒有該域名,所以報錯doesn't match any of the subject alternative names: []
印荔。當時有兩種思路,第一種:能不能在生產(chǎn)環(huán)境開啟訪問智能網(wǎng)關的權限,然后直接走域名,但是公司不知道基于什么考慮,不允許這樣做。第二種:http client是否支持不解析域名的操作,確實http client可以設置不解析host的策略,將其創(chuàng)建SSLClient的代碼調整為如下代碼:
private static CloseableHttpClient createSSLClientDefault() {
try {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
//不進行主機名驗證
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(builder.build(),
NoopHostnameVerifier.INSTANCE);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslConnectionSocketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(100);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.setDefaultCookieStore(new BasicCookieStore())
.setConnectionManager(cm).build();
return httpclient;
} catch (Exception e) {
logger.error("createSSLClientDefault error", e);
}
return null;
}
即可解決問題详羡。一個https的小問題確實耗費了我不少的時間,所以有必要記錄一下仍律。