一文解决Java中IP地址访问HTTPS接口的SSL证书验证问题

 更新时间:2025年12月19日 08:33:05   作者:一勺菠萝丶  
这篇文章主要为大家详细介绍了解决Java中IP地址访问HTTPS接口的SSL证书验证问题的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下

问题描述

在使用Java的RestTemplate访问HTTPS接口时,如果使用IP地址而不是域名,经常会遇到以下错误:

java.security.cert.CertificateException: No subject alternative names matching IP address 111.63.81.79 found
nested exception is javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names matching IP address 111.63.81.79 found

奇怪的是:使用Postman、curl等工具可以正常访问,但Java代码却报错。

问题原因

SSL证书验证机制

HTTPS协议在建立连接时,会进行SSL证书验证,主要包括两个方面:

  • 证书有效性验证:检查证书是否过期、是否被吊销等
  • 主机名验证:检查证书中的域名或IP地址是否与访问的地址匹配

为什么Postman可以,Java不行

  • Postman:默认情况下,Postman可能会跳过某些SSL验证(特别是开发环境)
  • Java:Java的SSL验证非常严格,会检查证书的Subject Alternative Names (SAN)字段
  • 问题根源:当使用IP地址访问时,如果证书的SAN中没有包含该IP地址,Java就会拒绝连接

证书的Subject Alternative Names

SSL证书中有一个字段叫Subject Alternative Names (SAN),它列出了该证书可以用于哪些域名或IP地址。例如:

Subject Alternative Names:
    DNS: example.com
    DNS: www.example.com
    IP Address: 192.168.1.1

如果证书中没有包含你访问的IP地址(如111.63.81.79),Java就会抛出上述异常。

解决方案

方案概述

我们需要配置RestTemplate,让它跳过SSL证书验证。注意:这种方法只适用于开发/测试环境,生产环境应该使用正确的证书。

完整代码实现

创建一个RestTemplateConfig配置类:

package com.example.zbx.config;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;

/**
 * RestTemplate配置类
 * 配置忽略SSL证书验证,支持使用IP地址访问HTTPS接口
 * @author lxy
 */
@Configuration
public class RestTemplateConfig {

    /**
     * 创建RestTemplate Bean
     * 配置忽略SSL证书验证,支持使用IP地址访问HTTPS接口
     * @return RestTemplate实例
     */
    @Bean
    public RestTemplate restTemplate() {
        try {
            // 创建完全信任所有证书的TrustManager
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }

                        @Override
                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            // 不进行任何检查,信任所有客户端证书
                        }

                        @Override
                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            // 不进行任何检查,信任所有服务器证书
                        }
                    }
            };

            // 创建SSL上下文,使用自定义的TrustManager
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

            // 创建SSL连接工厂,忽略主机名验证(包括IP地址验证)
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    NoopHostnameVerifier.INSTANCE
            );

            // 创建请求配置
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(10000)  // 连接超时10秒
                    .setConnectionRequestTimeout(10000)  // 请求超时10秒
                    .setSocketTimeout(30000)  // 读取超时30秒
                    .build();

            // 创建HttpClient
            CloseableHttpClient httpClient = HttpClients.custom()
                    .setSSLSocketFactory(sslSocketFactory)
                    .setDefaultRequestConfig(requestConfig)
                    .evictExpiredConnections()
                    .evictIdleConnections(30, java.util.concurrent.TimeUnit.SECONDS)
                    .build();

            // 创建请求工厂
            HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
            factory.setHttpClient(httpClient);
            factory.setConnectTimeout(10000);
            factory.setConnectionRequestTimeout(10000);
            factory.setReadTimeout(30000);

            // 创建RestTemplate
            return new RestTemplate(factory);
        } catch (Exception e) {
            throw new RuntimeException("创建RestTemplate失败", e);
        }
    }
}

关键点说明

1. 自定义TrustManager

TrustManager[] trustAllCerts = new TrustManager[]{
    new X509TrustManager() {
        @Override
        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            // 不进行任何检查,信任所有服务器证书
        }
        // ... 其他方法
    }
};

这个X509TrustManager会跳过所有证书验证,包括:

  • 证书是否过期
  • 证书是否被吊销
  • 证书的域名/IP是否匹配

2. 创建SSL上下文

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());

使用自定义的TrustManager初始化SSL上下文。

3. 忽略主机名验证

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
    sslContext,
    NoopHostnameVerifier.INSTANCE  // 不验证主机名
);

NoopHostnameVerifier.INSTANCE会跳过主机名验证,这样即使证书中没有对应的IP地址也能通过验证。

Maven依赖

确保你的pom.xml中包含以下依赖:

<!-- HttpClient4 (for RestTemplate SSL configuration) -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>

使用示例

配置完成后,直接使用RestTemplate即可,无需额外代码:

@Autowired
private RestTemplate restTemplate;

public void callApi() {
    String url = "https://111.63.81.79:10091/api/endpoint";
    ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
    // 处理响应...
}

注意事项

安全警告

  • 仅用于开发/测试环境:跳过SSL验证会带来安全风险,生产环境不应该使用
  • 中间人攻击风险:攻击者可以伪造证书,你的应用无法识别
  • 数据泄露风险:无法保证连接的安全性

生产环境建议

如果必须在生产环境使用,建议:

  • 使用正确的证书:让服务器提供包含IP地址的证书
  • 配置证书信任:只信任特定的证书颁发机构(CA)
  • 使用域名访问:尽量使用域名而不是IP地址

示例:生产环境的正确做法

// 只信任特定的证书
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream("truststore.jks"), "password".toCharArray());

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

常见问题

Q1: 为什么之前可以,现在不行了?

可能的原因:

  • Java版本更新,SSL验证更严格了
  • 服务器证书更新了,新证书没有包含IP地址
  • 网络环境变化,使用了不同的IP地址

Q2: 有没有更简单的办法?

对于开发环境,可以设置JVM参数(不推荐):

-Dcom.sun.net.ssl.checkRevocation=false

但这种方法不够灵活,建议使用配置类的方式。

Q3: 会影响其他HTTP请求吗?

不会。这个配置只影响使用RestTemplate的HTTPS请求,HTTP请求不受影响。

Q4: 如何验证配置是否生效?

在代码中添加日志,观察是否还有证书验证错误:

log.info("RestTemplate配置完成,SSL验证已禁用");

如果不再出现CertificateException,说明配置生效了。

总结

  • 问题根源:Java的SSL验证严格,证书中没有IP地址时会拒绝连接
  • 解决方案:配置RestTemplate跳过SSL证书验证
  • 关键代码:自定义X509TrustManager + NoopHostnameVerifier
  • 安全提醒:仅用于开发/测试环境,生产环境应使用正确的证书

到此这篇关于一文解决Java中IP地址访问HTTPS接口的SSL证书验证问题的文章就介绍到这了,更多相关Java解决SSL证书验证问题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 优化Java虚拟机总结(jvm调优)

    优化Java虚拟机总结(jvm调优)

    这篇文章主要介绍了优化Java虚拟机总结(jvm调优),具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • springBoot整合Eureka启动失败的解决方案

    springBoot整合Eureka启动失败的解决方案

    这篇文章主要介绍了springBoot整合Eureka启动失败的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • java算法题解LeetCode30包含min函数的栈实例

    java算法题解LeetCode30包含min函数的栈实例

    这篇文章主要为大家介绍了java算法题解LeetCode30包含min函数的栈实例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • 解决使用redisTemplate高并发下连接池满的问题

    解决使用redisTemplate高并发下连接池满的问题

    这篇文章主要介绍了解决使用redisTemplate高并发下连接池满的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • SpringCloud之Feign远程接口映射的实现

    SpringCloud之Feign远程接口映射的实现

    这篇文章主要介绍了SpringCloud之Feign远程接口映射的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • mybaties plus selectMaps和selectList的区别说明

    mybaties plus selectMaps和selectList的区别说明

    这篇文章主要介绍了mybaties plus selectMaps和selectList的区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • 解决SpringMVC同时接收Json和Restful时Request里有Map的问题

    解决SpringMVC同时接收Json和Restful时Request里有Map的问题

    今天小编就为大家分享一篇解决SpringMVC同时接收Json和Restful时Request里有Map的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • 代理模式之Java动态代理实现方法

    代理模式之Java动态代理实现方法

    今天一个偶然的机会我突然想看看JDK的动态代理,因为以前也知道一点,而且只是简单的想测试一下使用,使用很快里就写好了这么几个接口和类,需要的朋友可以参考下
    2012-11-11
  • Struts2学习教程之入门小白的开始基础

    Struts2学习教程之入门小白的开始基础

    struts2其实就是为我们封装了servlet,简化了jsp跳转的复杂操作,并且提供了易于编写的标签,可以快速开发view层的代码。下面这篇文章主要给各位想要学习Struts2的小白们详细介绍了关于Struts2入门的一些开始基础,需要的朋友可以参考下
    2018-04-04
  • springboot读取配置文件中的参数具体步骤

    springboot读取配置文件中的参数具体步骤

    在本篇文章里小编给大家分享了关于springboot读取配置文件中的参数的相关知识点内容,有需要的朋友们跟着学习下。
    2019-06-06

最新评论