使用HttpClient 4.5.5调用SOAP Web Service遇SSL证书匹配错误求助
嘿,这个问题我之前在对接内部SOAP服务的时候也碰到过,核心原因就是SSL证书的主机名校验不通过,我给你梳理下原因和几种可行的解决办法:
问题根源
你通过IP地址10.xx.xx.xx访问目标SOAP服务,但对方服务器的SSL证书里,**主题备用名称(SAN)**只配置了*.domain.vn和domain.vn,并没有包含你使用的这个IP地址。而HttpClient 4.5.5默认会严格校验请求的主机名(或IP)是否和证书里的SAN/CN匹配,所以直接抛出了SSLPeerUnverifiedException。
解决方案(按安全性从高到低排序)
1. 改用证书匹配的域名访问(最推荐)
如果你的网络环境允许,直接把请求URL换成domain.vn或者对应的子域名(比如soap.domain.vn),而不是用IP。这样证书的主机名校验会自动通过,不需要修改任何代码,这是最合规、最安全的做法。
2. 修改本地hosts文件映射(测试环境优选)
如果因为网络限制只能用IP访问,但又不想跳过SSL校验,可以修改本地hosts文件,把IP映射到证书里的域名:
- Windows系统:打开
C:\Windows\System32\drivers\etc\hosts(需要管理员权限) - Linux/macOS系统:编辑
/etc/hosts - 添加一行:
10.xx.xx.xx your-service.domain.vn - 之后用
your-service.domain.vn作为请求URL,本地会直接解析到目标IP,同时证书校验能匹配域名,完美解决问题。
3. 自定义HttpClient跳过主机名验证(仅测试/内部环境)
如果以上两种方式都不可行,只能临时跳过主机名校验(注意:生产环境绝对禁止这种操作,会暴露在SSL中间人攻击的风险下),可以修改你的HttpClient初始化代码:
static String callApi(String url, String requestXml) { String responseXml = ""; CloseableHttpClient httpClient = null; HttpPost httpPost; try { // 构建跳过主机名验证的SSL上下文 SSLContext sslContext = SSLContexts.createDefault(); // 自定义主机名验证器,始终返回验证通过 X509HostnameVerifier hostnameVerifier = new X509HostnameVerifier() { @Override public void verify(String host, SSLSocket ssl) throws IOException {} @Override public void verify(String host, X509Certificate cert) throws SSLException {} @Override public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {} @Override public boolean verify(String host, SSLSession session) { return true; } }; SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, hostnameVerifier); // 创建自定义HttpClient实例 httpClient = HttpClients.custom() .setSSLSocketFactory(sslSocketFactory) .build(); httpPost = new HttpPost(url); httpPost.setHeader("Content-Type", "text/xml; charset=utf-8"); httpPost.setHeader("x-ibm-client-id", Config.csp.validKey); StringEntity entity = new StringEntity(requestXml, "UTF-8"); httpPost.setEntity(entity); HttpResponse response = httpClient.execute(httpPost); HttpEntity responseEntity = response.getEntity(); responseXml = EntityUtils.toString(responseEntity, "UTF-8"); } catch (Exception ex) { log.error("调用SOAP API失败", ex); } finally { try { if (httpClient != null) httpClient.close(); } catch (Exception ex) { log.error("关闭HttpClient失败", ex); } } return responseXml; }
额外提一句:如果目标服务用的是自签名证书,你可能还需要添加信任所有证书的TrustManager,不过同样仅限测试环境使用。
内容的提问来源于stack exchange,提问作者chilinh12003
相关产品推荐
相关产品推荐

