使用HttpClient发Post请求遇SSLPeerUnverifiedException,如何匹配通配符证书?
看起来你遇到的SSLPeerUnverifiedException有点反常——毕竟showcase.example.com和证书里的*.example.com通配符理论上是完全匹配的。这个问题通常出在HttpClient默认的主机名验证器上,要么是版本偏旧导致通配符匹配逻辑有bug,要么是需要我们手动配置更灵活的验证策略。下面给你几个可行的解决方案:
方案一:配置支持通配符的验证器(推荐生产环境)
如果你的HttpClient版本不算太新,默认验证器可能对通配符的支持不够完善,我们可以手动指定标准的验证器,或者自定义安全的验证逻辑:
1. 构建SSL上下文
先创建标准的SSL上下文,生产环境建议使用系统默认的信任存储(只信任权威CA签发的证书):
import org.apache.http.ssl.SSLContexts; import javax.net.ssl.SSLContext; // 使用系统默认的信任策略创建SSL上下文 SSLContext sslContext = SSLContexts.createDefault();
2. 指定支持通配符的主机名验证器
DefaultHostnameVerifier本身就遵循标准的通配符匹配规则(比如*.example.com可以匹配所有二级域名),我们直接用它替换默认验证器即可:
import org.apache.http.conn.ssl.DefaultHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; DefaultHostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, hostnameVerifier);
如果需要更严格的自定义规则(比如只允许特定后缀的域名),也可以自己实现HostnameVerifier:
import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; HostnameVerifier customVerifier = new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { // 自定义规则:仅允许.example.com结尾的域名通过验证 return hostname.endsWith(".example.com"); } };
3. 构建配置好的HttpClient
把自定义的SSL连接工厂注入到HttpClientBuilder中:
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.client.HttpClient; HttpClient httpClient = HttpClientBuilder.create() .setSSLSocketFactory(sslSocketFactory) .build();
后续的Post请求代码保持你原来的写法即可。
方案二:临时跳过主机名验证(仅测试环境使用)
如果你只是在测试环境调试,不想纠结验证逻辑,可以用NoopHostnameVerifier完全跳过主机名检查,但绝对不能在生产环境使用,这会带来严重的安全风险:
import org.apache.http.conn.ssl.NoopHostnameVerifier; SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory( sslContext, NoopHostnameVerifier.INSTANCE); HttpClient httpClient = HttpClientBuilder.create() .setSSLSocketFactory(sslSocketFactory) .build();
方案三:升级HttpClient版本
如果你的HttpClient版本比较老(比如4.2及更早),默认的主机名验证器确实存在通配符匹配的bug。升级到最新的4.x稳定版(比如4.5.x)或者5.x版本,就能自动解决这个问题,不需要额外配置。
内容的提问来源于stack exchange,提问作者charlie20171

