创建信任所有证书的HttpClient:Java 实现 HTTPS 接口调用,等效于curl -k

发布于:2025-05-23 ⋅ 阅读:(18) ⋅ 点赞:(0)

在 Java 生态中,HttpClientFeign 都是调用第三方接口的常用工具,但它们的定位、设计理念和使用场景有显著差异。以下是详细对比:


DIFF1. 定位与抽象层级

特性 HttpClient Feign
层级 底层 HTTP 客户端库(处理原始请求/响应) 声明式 HTTP 客户端(基于接口和注解)
核心目标 提供灵活的 HTTP 通信能力 简化 REST 客户端开发,隐藏 HTTP 细节
依赖关系 独立使用(如 Apache HttpClient/OkHttp) 需依赖 HttpClient(默认集成 Ribbon/OkHttp)

DIFF2.性能与资源**

维度 HttpClient Feign
启动开销 低(直接使用) 较高(需生成动态代理类)
运行时性能 更优(无额外抽象层) 轻微损耗(反射/动态代理)
连接池 需手动配置 PoolingHttpClientConnectionManager 自动复用底层 HttpClient 连接池
本文仅对HttpClient调用https接口调用做一个总结,先上代码:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustAllStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;

/**
 * HTTPS 接口调用工具类
 * 包含 GET 和 POST 请求方法,支持自定义请求头和跳过证书验证(仅限测试环境)
 */
public class HttpsClientUtil {

    /**
     * 发送 HTTPS GET 请求
     * @param url 请求地址
     * @param headers 请求头数组,格式为 {"key1:value1", "key2:value2"}
     * @return 响应内容
     * @throws Exception 如果请求过程中发生错误
     */
    public static String doGet(String url, String[] headers) throws Exception {
        // 1. 创建支持 HTTPS 的 HttpClient 实例
        try (CloseableHttpClient httpClient = createHttpsClient()) {
            // 2. 创建 GET 请求对象
            HttpGet httpGet = new HttpGet(url);
            
            // 3. 设置请求头(如果有)
            if (headers != null) {
                for (String header : headers) {
                    String[] kv = header.split(":");
                    if (kv.length == 2) {
                        httpGet.addHeader(kv[0].trim(), kv[1].trim());
                    }
                }
            }
            
            // 4. 执行请求并获取响应
            HttpResponse response = httpClient.execute(httpGet);
            
            // 5. 解析响应内容
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            }
            return null;
        }
    }

    /**
     * 发送 HTTPS POST 请求(JSON格式)
     * @param url 请求地址
     * @param jsonBody JSON格式的请求体
     * @param headers 请求头数组
     * @return 响应内容
     * @throws Exception 如果请求过程中发生错误
     */
    public static String doPost(String url, String jsonBody, String[] headers) throws Exception {
        try (CloseableHttpClient httpClient = createHttpsClient()) {
            // 1. 创建 POST 请求对象
            HttpPost httpPost = new HttpPost(url);
            
            // 2. 设置请求头和请求体
            httpPost.addHeader("Content-Type", "application/json");
            if (headers != null) {
                for (String header : headers) {
                    String[] kv = header.split(":");
                    if (kv.length == 2) {
                        httpPost.addHeader(kv[0].trim(), kv[1].trim());
                    }
                }
            }
            
            // 3. 设置 JSON 请求体
            if (jsonBody != null) {
                httpPost.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
            }
            
            // 4. 执行请求并获取响应
            HttpResponse response = httpClient.execute(httpPost);
            
            // 5. 解析响应内容
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                return EntityUtils.toString(entity, StandardCharsets.UTF_8);
            }
            return null;
        }
    }

    /**
     * 创建支持 HTTPS 的 HttpClient 实例
     * 注意:此方法跳过证书验证,仅适用于测试环境!
     * 生产环境应使用正确的证书验证方式
     */
    private static CloseableHttpClient createHttpsClient() throws Exception {
        // 1. 创建 SSLContext,配置信任所有证书(不安全,仅测试用)
        SSLContext sslContext = SSLContextBuilder.create()
                .loadTrustMaterial(new TrustAllStrategy()) // 信任所有证书
                .build();
        
        // 2. 创建 SSL 连接工厂,跳过主机名验证
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
                sslContext,
                NoopHostnameVerifier.INSTANCE); // 跳过主机名验证
        
        // 3. 配置请求超时参数
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(5000) // 连接超时 5秒
                .setSocketTimeout(10000) // 请求超时 10秒
                .setConnectionRequestTimeout(2000) // 从连接池获取连接超时 2秒
                .build();
        
        // 4. 创建 HttpClient 实例
        return HttpClients.custom()
                .setSSLSocketFactory(sslSocketFactory) // 设置 SSL 工厂
                .setDefaultRequestConfig(requestConfig) // 设置超时配置
                .build();
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        try {
            // 测试 GET 请求
            String getUrl = "https://jsonplaceholder.typicode.com/posts/1";
            String getResponse = doGet(getUrl, null);
            System.out.println("GET 响应:\n" + getResponse);
            
            // 测试 POST 请求
            String postUrl = "https://jsonplaceholder.typicode.com/posts";
            String jsonBody = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";
            String[] headers = {"Authorization: Bearer token123"};
            String postResponse = doPost(postUrl, jsonBody, headers);
            System.out.println("\nPOST 响应:\n" + postResponse);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

关键代码解析

1. 创建信任所有证书的 SSLContext

SSLContext sslContext = SSLContextBuilder.create()
        .loadTrustMaterial(new TrustAllStrategy()) // 信任所有证书
        .build();
  • TrustAllStrategy 是 Apache HttpClient 提供的策略,会信任所有证书(包括自签名和过期证书)
  • 生产环境警告:这种配置不安全,只应在测试环境使用

2. 配置 SSL 连接工厂

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
        sslContext,
        NoopHostnameVerifier.INSTANCE); // 跳过主机名验证
  • NoopHostnameVerifier 会跳过主机名验证
  • 实际项目中应该使用 DefaultHostnameVerifier

3. 设置请求超时

RequestConfig requestConfig = RequestConfig.custom()
        .setConnectTimeout(5000) // 连接超时 5秒
        .setSocketTimeout(10000) // 请求超时 10秒
        .setConnectionRequestTimeout(2000) // 从连接池获取连接超时 2秒
        .build();
  • 防止因网络问题导致线程长时间阻塞

4. 创建 HttpClient 实例

return HttpClients.custom()
        .setSSLSocketFactory(sslSocketFactory) // 设置 SSL 工厂
        .setDefaultRequestConfig(requestConfig) // 设置超时配置
        .build();
  • 使用 try-with-resources 确保资源自动关闭

生产环境建议

  1. 不要跳过证书验证
    应配置正确的信任库:

    SSLContext sslContext = SSLContextBuilder.create()
            .loadTrustMaterial(new File("/path/to/truststore.jks"), "password".toCharArray())
            .build();
    
  2. 使用连接池
    对于高频请求,应配置连接池:

    PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
    connManager.setMaxTotal(100);
    connManager.setDefaultMaxPerRoute(20);
    
  3. 添加重试机制
    对临时性网络错误实现自动重试

  4. 使用更现代的 HTTP 客户端
    考虑使用 OkHttp 或 Java 11+ 的 HttpClient

这个实现提供了完整的 HTTPS 调用功能,可以直接用于项目开发(测试环境)。


网站公告

今日签到

点亮在社区的每一天
去签到