首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Java HttpClient 4.x 發送 HTTPS POST 請求:信任所有憑證完整方案

     編輯:關於C語言

應用場景

開發/測試環境常遇到伺服器使用**自簽名**、**過期**、**域名不匹配**的 SSL 憑證,導致 javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated。本文提供基於 **Apache HttpComponents Client 4.x** 的完整解決方案:自定義 TrustManager 信任所有憑證,註冊至 HttpClient 實現 HTTPS POST 請求。

核心原理

  1. 建立不驗證憑證鏈的 X509TrustManager(三個方法體為空)。
  2. 用其初始化 SSLContext(協議 TLS)。
  3. 建立 SSLSocketFactory 並註冊至 SchemeRegistry(協議 https、埠 443)。
  4. 正常構建 HttpPost、設定參數、執行請求。

完整可執行類


package com.example.util;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HttpsPostUtil {

    /**
     * 向 HTTPS URL 發送 POST 表單請求,信任所有伺服器憑證
     *
     * @param url    請求地址
     * @param params 表單參數 Map
     * @return 響應字串,失敗返回 null
     */
    public static String sendHttpsPost(String url, Map params) {
        HttpClient httpClient = new DefaultHttpClient();
        String responseContent = null;

        try {
            // 1. 建立信任所有憑證的 TrustManager
            TrustManager trustAll = new X509TrustManager() {
                @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
                @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
                @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
            };

            // 2. 初始化 SSLContext
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustAll}, null);

            // 3. 建立 SSLSocketFactory 並註冊
            SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext);
            httpClient.getConnectionManager().getSchemeRegistry()
                      .register(new Scheme("https", 443, socketFactory));

            // 4. 構建 POST 請求
            HttpPost httpPost = new HttpPost(url);
            List formParams = new ArrayList<>();
            for (Map.Entry entry : params.entrySet()) {
                formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            httpPost.setEntity(new UrlEncodedFormEntity(formParams, "UTF-8"));

            // 5. 執行並處理響應
            HttpResponse response = httpClient.execute(httpPost);
            HttpEntity entity = response.getEntity();

            if (entity != null) {
                responseContent = EntityUtils.toString(entity, "UTF-8");
                EntityUtils.consume(entity); // 釋放連接
            }

            // 除錯資訊
            System.out.println("請求地址: " + httpPost.getURI());
            System.out.println("響應狀態: " + response.getStatusLine());
            System.out.println("響應內容: " + responseContent);

        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            httpClient.getConnectionManager().shutdown(); // 關鍵:釋放連接管理器資源
        }
        return responseContent;
    }

    // 測試主方法
    public static void main(String[] args) throws Exception {
        Map params = new HashMap<>();
        params.put("TransName", "IQSR");
        params.put("Plain", "transId=IQSR~|~originalorderId=2012~|~originalTransAmt=~|~merURL=");
        params.put("Signature", "9b759887e6ca9d4c24509d22ee4d22494d0dd2dfbdbeaab3545c1acee62eec7");

        String result = sendHttpsPost("https://www.cebbank.com/per/QueryMerchantEpay.do", params);
        System.out.println("最終結果: " + result);
    }
}

關鍵細節與避坑指南

問題解決
HttpClient 4.3+ 棄用 SchemeRegistry改用 HttpClientBuilder + SSLConnectionSocketFactory + RegistryBuilder(見下方現代寫法)
多執行緒共用 HttpClientDefaultHttpClient 線程安全,但建議用 PoolingHttpClientConnectionManager 管理連接池
憑信所有憑證的安全風險**僅限開發/測試環境**!生產環境必須載入正規 CA 憑證或 Pinning
中文參數亂碼UrlEncodedFormEntity(formParams, "UTF-8") 明確指定編碼
連接洩漏finally 中 shutdown() 或 close() 必須執行

現代寫法(HttpClient 4.5+ / 5.x)


import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

// ...
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustAll}, null);

SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
    sslContext, NoopHostnameVerifier.INSTANCE);

Registry registry = RegistryBuilder.create()
    .register("http", PlainConnectionSocketFactory.getSocketFactory())
    .register("https", sslSocketFactory)
    .build();

PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(50);

HttpClient httpClient = HttpClientBuilder.create()
    .setConnectionManager(cm)
    .setDefaultRequestConfig(RequestConfig.custom()
        .setConnectTimeout(5000)
        .setSocketTimeout(10000)
        .build())
    .build();
// 後續 httpPost 執行邏輯相同

總結

自定義 TrustManager 繞過憑證驗證是解決開發環境 HTTPS 請求失效的標準手段。掌握 SSLContext → SocketFactory → SchemeRegistry/ConnectionManager 的註冊鏈路,並注意**資源釋放**、**線程安全**、**生產環境禁用**,即可從容應對各類 HTTPS 整合場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved