
本文介绍在 Java 应用中无法通过 DNS 解析服务 FQDN 时的替代方案,重点说明为何直接使用 IP 地址会导致 SSL 握手失败,并提供基于 HostnameVerifier 的临时解决方案及更优的生产级实践建议。
本文介绍在 java 应用中无法通过 dns 解析服务 fqdn 时的替代方案,重点说明为何直接使用 ip 地址会导致 ssl 握手失败,并提供基于 `hostnameverifier` 的临时解决方案及更优的生产级实践建议。
在 Java 网络编程中,当目标服务的 FQDN(如 abc.xyz.com)因 DNS 不可达或网络策略限制而无法解析时,开发者常尝试直接使用 IP 地址发起 HTTPS 请求。然而,这会触发 javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address... 异常——因为现代 TLS 证书通常仅包含域名(DNS Name)类型的 Subject Alternative Name(SAN),不包含 IP 地址条目,而 JDK 默认严格校验证书与请求主机名的一致性。
❌ 错误做法:禁用主机名验证(仅限测试)
虽然以下代码能“绕过”异常,但绝对不可用于生产环境:
HttpsURLConnection urlConnection = (HttpsURLConnection) new URL("https://100.70.125.57:443/json").openConnection();
urlConnection.setHostnameVerifier((hostname, session) -> true); // ⚠️ 危险!完全禁用校验
该方式等同于关闭 TLS 安全保障,使应用暴露于中间人攻击(MITM)风险中,违反 OWASP 和 PCI-DSS 等安全规范。
✅ 推荐方案:自定义 HostnameVerifier(谨慎使用)
若确需基于 IP 访问且证书 SAN 中明确包含该 IP 地址(需运维团队配合签发),可编写精准校验逻辑:
HostnameVerifier strictIPVerifier = (hostname, session) -> {
// 仅当 hostname 是预期 IP 且证书 SAN 中存在该 IP 时才放行
if ("100.70.125.57".equals(hostname)) {
try {
Certificate[] certs = session.getPeerCertificates();
if (certs.length > 0 && certs[0] instanceof X509Certificate) {
X509Certificate cert = (X509Certificate) certs[0];
Collection<list>> sans = cert.getSubjectAlternativeNames();
if (sans != null) {
for (List> san : sans) {
if (san.size() >= 2 && Integer.valueOf(san.get(0).toString()) == 7) { // 7 = iPAddress type
if ("100.70.125.57".equals(san.get(1))) {
return true;
}
}
}
}
}
} catch (Exception ignored) {}
}
return false;
};
urlConnection.setHostnameVerifier(strictIPVerifier);</list>
? 更优的生产级解决方案
- 修复 DNS 基础设施:确保应用所在环境能正常解析 abc.xyz.com(检查 /etc/resolv.conf、DNS 服务器可达性、防火墙策略);
- 使用 Service Mesh 或代理:通过 Istio/Envoy 等透明处理 DNS 解析与 TLS 终止;
-
配置 JVM 系统属性(适用于已知可信 DNS):
java -Dsun.net.inetaddr.ttl=30 -Dnetworkaddress.cache.ttl=30 MyApp
-
集成轻量级 DNS 客户端库(如 dnsjava),实现应用内可控解析:
Lookup lookup = new Lookup("abc.xyz.com", Type.A); Record[] records = lookup.run(); if (records != null) { String ip = ((ARecord) records[0]).getAddress().getHostAddress(); // 后续构造 URL 并发起 HTTPS 请求 }
总结:setHostnameVerifier 是调试阶段的权宜之计,而非解决方案。生产环境中应优先保障 DNS 可靠性,并确保 TLS 证书覆盖实际访问方式(域名优先,IP 地址需显式声明于 SAN)。任何绕过主机名验证的行为都必须经过安全评审,并配套网络层防护措施。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











