HttpClient實現https調用


  • 在HttpClient 4.x版本中引入了大量的構造器設計模式

  • https請求建立詳解

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


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 registryBuilder = RegistryBuilder.create();

ConnectionSocketFactory plainSF =newPlainConnectionSocketFactory();

registryBuilder.register("http", plainSF);

//指定信任密鑰存儲對象和連接套接字工廠

try{

KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());

SSLContext sslContext = SSLContexts.custom().useTLS().loadTrustMaterial(trustStore,newAnyTrustStrategy()).build();

LayeredConnectionSocketFactory sslSF =newSSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

registryBuilder.register("https", sslSF);

}catch(KeyStoreException e) {

thrownewRuntimeException(e);

}catch(KeyManagementException e) {

thrownewRuntimeException(e);

}catch(NoSuchAlgorithmException e) {

thrownewRuntimeException(e);

}

Registry registry = registryBuilder.build();

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

//設置連接管理器

PoolingHttpClientConnectionManager connManager =newPoolingHttpClientConnectionManager(registry);

connManager.setDefaultConnectionConfig(connConfig);

connManager.setDefaultSocketConfig(socketConfig);

//構建客戶端

HttpClient client= HttpClientBuilder.create().setConnectionManager(connManager).build();

為了讓我們的HttpClient具有多線程處理的能力,連接管理器選用了PoolingHttpClientConnectionManager,將協議注冊信息傳入連接管理器,最后再次利用構造器的模式創建出我們需要的HttpClient。隨后的GET/POST請求發起方法http和https之間沒有差異。
為了驗證我們的代碼是否成功,可以做下JUnit單元測試:

@Test

publicvoiddoTest()throwsClientProtocolException, URISyntaxException, IOException{

HttpUtil util = HttpUtil.getInstance();

InputStream in = util.doGet("https://kyfw.12306.cn/otn/leftTicket/init");

String retVal = HttpUtil.readStream(in, HttpUtil.defaultEncoding);

System.out.println(retVal);

}

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

  • 有朋友反饋說提供的工具類中沒有直接POST JSON對象的方法,下面我提供一下基礎方法,供參考(此代碼未包含在下文的共享資源中,請自行補充進去)
/**

* 基本Post請求

* @param url 請求url

* @param queryParams 請求頭的查詢參數

* @param json 直接放入post請求體中的文本(請使用JSON)

* @return

* @throws URISyntaxException

* @throws UnsupportedEncodingException

*/

publicHttpResponse doPostBasic(String url, Map queryParams, String json)throwsURISyntaxException, ClientProtocolException, IOException{

HttpPost pm =newHttpPost();

URIBuilder builder =newURIBuilder(url);

//填入查詢參數

if(MapUtils.isNotEmpty(queryParams)){

builder.setParameters(HttpUtil.paramsConverter(queryParams));

}

pm.setURI(builder.build());

//填入post json數據

if(StringUtils.isNotBlank(json)){

//下面的ContentType完整類名為:org.apache.http.entity.ContentType

pm.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));

}

return client.execute(pm);

}

  • 下面再列舉一個傳入指定秘鑰的https訪問方法
public void initSSLConfigForTwoWay() throws Exception {

        HttpClientBuilder b = HttpClientBuilder.create();
        // 1 Import your own certificate
//		String demo_base_Path = System.getProperty("user.dir");
//		String demo_base_Path = getClass().getClassLoader().getResource("").getPath();
//		String selfcertpath = demo_base_Path +  Constant.SELFCERTPATH;
//		String trustcapath = demo_base_Path +  Constant.TRUSTCAPATH;

//		String selfcertpath = demo_base_Path + Constant.SELFCERTPATH;
//		String trustcapath = demo_base_Path + Constant.TRUSTCAPATH;

        KeyStore selfCert = KeyStore.getInstance("pkcs12");
        selfCert.load(getClass().getClassLoader().getResourceAsStream(NbConstant.CERT_FLODER + File.separatorChar + NbConstant.SELFCERTPATH),
                NbConstant.SELFCERTPWD.toCharArray());
        KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
        kmf.init(selfCert, NbConstant.SELFCERTPWD.toCharArray());

        // 2 Import the CA certificate of the server,
        KeyStore caCert = KeyStore.getInstance("jks");
        caCert.load(getClass().getClassLoader().getResourceAsStream(NbConstant.CERT_FLODER + File.separatorChar + NbConstant.TRUSTCAPATH), NbConstant.TRUSTCAPWD.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("sunx509");
        tmf.init(caCert);

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

        // 3 Set the domain name to not verify
        // (Non-commercial IoT platform, no use domain name access generally.)
        SSLSocketFactory ssf = new SSLSocketFactory(sc, new String[]{"TLSv1.2", "TLSv1.1", "TLSv1"}, null,
                SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        // If the platform has already applied for a domain name which matches
        // the domain name in the certificate information, the certificate
        // domain name check can be enabled (open by default)
        // SSLSocketFactory ssf = new SSLSocketFactory(sc);

//		ClientConnectionManager ccm = this.getConnectionManager();
//		SchemeRegistry sr = ccm.getSchemeRegistry();
//		sr.register(new Scheme("https", 8743, ssf));


        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", ssf)
                .build();

        //      -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager(connMgr);

        // finally, build the HttpClient;
        //      -- done!


//		httpClient = new DefaultHttpClient(ccm);
        httpClient = b.build();
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM