歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

基於HttpClient 4.3的可訪問自簽名HTTPS站點的新版工具類

HttpClient在當今Java應用中的位置越來越重要。從該項目的變遷過程我們不難發現,其已經從apache-commons眾多的子項目中剝離,一躍成為如今的頂級項目,可見它的分量。然而隨著項目的升級和架構的調整,很多以前常用的類和方法都已被打上了@Deprecated注解,作為一個有代碼潔癖的程序猿,我們也有必要升級一下工具類,讓代碼更加整潔。

另外在項目中正好需要訪問https協議的接口,而對應的服務器沒有購買商業CA頒發的正式受信證書,只是做了個自簽名(聯想一下12306網站購票時提示的那個警告信息),默認情況下通過HttpClient訪問會拋出異常,在本文中也給出了解決辦法。

在HttpClient 4.x版本中引入了大量的構造器設計模式,很多的配置都不建議直接new出來,而且相關的API也有所改動,例如連接參數,以前是直接new出HttpConnectionParams對象後通過set方法逐一設置屬性,現在有了構造器,可以通過如下方式進行構造:

ConnectionConfig.custom().setCharset(Charsets.toCharset(defaultEncoding)).build();

SocketConfig.custom().setSoTimeout(100000).build();

基本情況就是這樣,接下來談下如何使用新的HttpClient來訪問自簽名https接口。

參閱CSDN博主noodies代碼,發現實現訪問自簽名https的要點就是建立一個自定義的SSLContext對象,該對象要有可以存儲信任密鑰的容器,還要有判斷當前連接是否受信任的策略,以及在SSL連接工廠中取消對所有主機名的驗證。他的代碼將會在本文最後貼出來,以下代碼均針對新HttpClient。

首先建立一個信任任何密鑰的策略。代碼很簡單,不去考慮證書鏈和授權類型,均認為是受信任的:

 class AnyTrustStrategy implements TrustStrategy{
 
 @Override
 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
  return true;
 }
 
}

HttpClient既能處理常規http協議,又能支持https,根源在於在連接管理器中注冊了不同的連接創建工廠。當訪問url的schema為http時,調用明文連接套節工廠來建立連接;當訪問url的schema為https時,調用SSL連接套接字工廠來建立連接。對於http的連接我們不做修改,只針對使用SSL的https連接來進行自定義:

RegistryBuilder<ConnectionSocketFactory> registryBuilder = RegistryBuilder.<ConnectionSocketFactory>create();
ConnectionSocketFactory plainSF = new PlainConnectionSocketFactory();
registryBuilder.register("http", plainSF);
//指定信任密鑰存儲對象和連接套接字工廠
try {
 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
 SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore, new AnyTrustStrategy()).build();
 LayeredConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
 registryBuilder.register("https", sslSF);
} catch (KeyStoreException e) {
 throw new RuntimeException(e);
} catch (KeyManagementException e) {
 throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
 throw new RuntimeException(e);
}
Registry<ConnectionSocketFactory> registry = registryBuilder.build();

在上述代碼中可以看到,首先建立了一個密鑰存儲容器,隨後讓SSLContext開啟TLS,並將密鑰存儲容器和信任任何主機的策略加載到該上下文中。構造SSL連接工廠時,將自定義的上下文和允許任何主機名通過校驗的指令一並傳入。最後將這樣一個自定義的SSL連接工廠注冊到https協議上。

前期准備已經完成,接下來我們要獲得HttpClient對象:

//設置連接管理器
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry);
connManager.setDefaultConnectionConfig(connConfig);
connManager.setDefaultSocketConfig(socketConfig);
//構建客戶端
HttpClient client= HttpClientBuilder.create().setConnectionManager(connManager).build();

為了讓我們的HttpClient具有多線程處理的能力,連接管理器選用了PoolingHttpClientConnectionManager,將協議注冊信息傳入連接管理器,最後再次利用構造器的模式創建出我們需要的HttpClient。隨後的GET/POST請求發起方法http和https之間沒有差異。

為了驗證我們的代碼是否成功,可以做下JUnit單元測試:

import sys
class progressbar(object):
    def __init__(self, finalcount, block_char='.'):
        self.finalcount = finalcount
        self.blockcount = 0
        self.block = block_char
        self.f = sys.stdout
        if not self.finalcount: return
        self.f.write('\n------------------ % Progress -------------------1\n')
        self.f.write('    1    2    3    4    5    6    7    8    9    0\n')
        self.f.write('----0----0----0----0----0----0----0----0----0----0\n')
    def progress(self, count):
        count = min(count, self.finalcount)
        if self.finalcount:
            percentcomplete = int(round(100.0*count/self.finalcount))
            if percentcomplete < 1: percentcomplete = 1
        else:
            percentcomplete=100
        blockcount = int(percentcomplete//2)
        if blockcount <= self.blockcount:
            return
        for i in range(self.blockcount, blockcount):
            self.f.write(self.block)
        self.f.flush()
        self.blockcount = blockcount
        if percentcomplete == 100:
            self.f.write("\n")

if __name__ == "__main__":
    from time import sleep
    pb = progressbar(8, "*")
    for count in range(1, 9):
        pb.progress(count)
        sleep(0.2)
    pb = progressbar(100)
    pb.progress(20)
    sleep(0.3)
    pb.progress(47)
    sleep(0.3)
    pb.progress(90)
    sleep(0.3)
    pb.progress(100)
    print "testing 1:"
    pb = progressbar(1)
    pb.progress(1)

執行後可以在控制台看到12306余票查詢界面的html代碼

為了方便大家使用,本人將封裝好的代碼上傳到了Linux公社資源共享中,歡迎下載。

下載地址

------------------------------------------分割線------------------------------------------

免費下載地址在 http://linux.linuxidc.com/

用戶名與密碼都是www.linuxidc.com

具體下載目錄在 /2015年資料/1月/2日/基於HttpClient 4.3的可訪問自簽名HTTPS站點的新版工具類

下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm

------------------------------------------分割線------------------------------------------

最後參考了這篇文章代碼提供了思路:http://www.linuxidc.com/Linux/2015-01/111122.htm

本文中的部分代碼參閱了HttpClient 4.3.5官方文檔的2.7節關於Connection socket factories的內容

Android使用HttpURLConnection下載圖片 http://www.linuxidc.com/Linux/2014-05/101854.htm

Adroid 4.0 HttpURLConnection拋異常解決方法 http://www.linuxidc.com/Linux/2013-03/81085.htm

node.js+Android(使用HttpURLConnection和HttpClient)實現文件上傳 http://www.linuxidc.com/Linux/2012-02/53532.htm

在Android上用HttpURLConnection獲取網頁內容 http://www.linuxidc.com/Linux/2011-08/41211.htm

Java使用HttpURLConnection上傳文件 http://www.linuxidc.com/Linux/2014-08/104946.htm

使用HttpClient實現文件的上傳下載 http://www.linuxidc.com/Linux/2014-07/104303.htm

Android 實現 HttpClient 請求Https http://www.linuxidc.com/Linux/2014-05/102306.htm

Android使用HttpClient下載圖片 http://www.linuxidc.com/Linux/2014-05/101855.htm

Copyright © Linux教程網 All Rights Reserved