java實現 HTTP/HTTPS請求繞過證書檢測代碼實現
1、開發需求
需要實現在服務端發起HTTP/HTTPS請求,訪問其他程序資源。
2、URLConnection和HTTPClient的比較
HttpClient是個很不錯的開源框架,封裝了訪問http的請求頭,參數,內容體,響應等等,
DefaultHttpClient和它的兄弟AndroidHttpClient都是HttpClient具體的實現類,它們都擁有眾多的API,而且實現比較穩定,bug數量也很少。
3、使用Apache的HttpClient發送GET和POST請求
1. 使用幫助類HttpClients創建CloseableHttpClient對象.
2. 基於要發送的HTTP請求類型創建HttpGet或者HttpPost實例.
3. 使用addHeader方法添加請求頭部,諸如User-Agent, Accept-Encoding等參數.
4. 對於POST請求,創建NameValuePair列表,並添加所有的表單參數.然后把它填充進HttpPost實體.
5. 通過執行此HttpGet或者HttpPost請求獲取CloseableHttpResponse實例
6. 從此CloseableHttpResponse實例中獲取狀態碼,錯誤信息,以及響應頁面等等.
7. 最后關閉HttpClient資源.
4、SSL與TLS的區別以及介紹
SSL是Netscape公司所提出的安全保密協議,在瀏覽器(如Internet Explorer、Netscape Navigator)和Web服務器(如Netscape的Netscape Enterprise Server、ColdFusion Server等等)之間構造安全通道來進行數據傳輸,SSL運行在TCP/IP層之上、應用層之下,為應用程序提供加密數據通道,它采用了RC4、MD5以及RSA等加密算法,使用40 位的密鑰,適用於商業信息的加密。
同時,Netscape公司相應開發了HTTPS協議並內置於其瀏覽器中。
HTTPS實際上就是HTTP over SSL,它使用默認端口443,而不是像HTTP那樣使用端口80來和TCP/IP進行通信。
HTTPS協議使用SSL在發送方把原始數據進行加密,然后在接受方進行解密,加密和解密需要發送方和接受方通過交換共知的密鑰來實現,因此,所傳送的數據不容易被網絡黑客截獲和解密。 然而,加密和解密過程需要耗費系統大量的開銷,嚴重降低機器的性能,相關測試數據表明使用HTTPS協議傳輸數據的工作效率只有使用HTTP協議傳輸的十分之一。假如為了安全保密,將一個網站所有的Web應用都啟用SSL技術來加密,並使用HTTPS協議進行傳輸,那么該網站的性能和效率將會大大降低,而且沒有這個必要,因為一般來說並不是所有數據都要求那么高的安全保密級別。
TLS
安全傳輸層協議
TLS:Transport Layer Security
安全傳輸層協議(TLS)用於在兩個通信應用程序之間提供保密性和數據完整性。該協議由兩層組成: TLS 記錄協議(TLS Record)和 TLS 握手協議(TLS Handshake)。較低的層為 TLS 記錄協議,位於某個可靠的傳輸協議(例如 TCP)上面。
5、 post請求封裝的工具類
import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.KeyManagementException; import java.security.KeyStoreException; 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; import java.util.Map.Entry; import com.sun.corba.se.impl.orbutil.threadpool.TimeoutException; import org.apache.commons.httpclient.util.TimeoutController; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import cn.qtone.hjy.service.bean.yd.sync.AccountPasswordInfoSyncItem; import cn.qtone.hjy.service.bean.yd.sync.OrderInfoSyncItem; import cn.qtone.hjy.service.bean.yd.sync.YdOrderRelationshipInfoSyncItem; import cn.qtone.hjy.service.core.DES; import cn.qtone.hjy.service.dao.YdEduDao; import cn.qtone.util.SpringUtil; import com.alibaba.fastjson.JSONObject; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; public class HttpClientUtil { static Logger log = Logger.getLogger(HttpClientUtil.class) ; private static RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(10000).build();//設置請求和傳輸超時時間 private static CloseableHttpClient httpclient = HttpClients.createDefault(); public static String send(String httpUrl, String message) throws IOException { String result = null ; HttpPost httpPost = new HttpPost(httpUrl); //設置數據讀取超時5s 傳輸超時5s 鏈接請求超時5s RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(5000) .setConnectTimeout(5000) .setConnectionRequestTimeout(5000) .build(); httpPost.setConfig(requestConfig) ; message = URLEncoder.encode(message, "UTF-8") ; StringEntity entity = new StringEntity(message); httpPost.setEntity(entity); CloseableHttpResponse response = httpclient.execute(httpPost); BufferedReader in = null ; try { InputStream content = response.getEntity().getContent() ; in = new BufferedReader(new InputStreamReader(content)); StringBuilder sb = new StringBuilder(); String line = "" ; while ((line = in.readLine()) != null) { sb.append(line); } result = sb.toString() ; System.out.println("接收原始報文:" + URLDecoder.decode(result, "UTF-8")) ; } finally { EntityUtils.consume(response.getEntity()); response.close(); } return result ; } public static String post(String httpUrl, String message) throws Exception { String result = null ; CloseableHttpClient httpclient = HttpClients.createDefault(); BufferedReader in = null ; HttpPost httpPost = new HttpPost(httpUrl); httpPost.setConfig(requestConfig); List <NameValuePair> nvps = new ArrayList <NameValuePair>(); nvps.add(new BasicNameValuePair("tokenId", DES.encrypt(message))); httpPost.setEntity(new UrlEncodedFormEntity(nvps)); try { System.out.println("發送報文:" + message); System.out.println("發送報文:" + DES.encrypt(message)) ; CloseableHttpResponse response = httpclient.execute(httpPost); InputStream content = response.getEntity().getContent() ; in = new BufferedReader(new InputStreamReader(content, "UTF-8")); StringBuilder sb = new StringBuilder(); String line = "" ; while ((line = in.readLine()) != null) { sb.append(line); } System.out.println("響應報文:" + sb.toString()) ; //result = URLDecoder.decode(sb.toString(), "UTF-8") ; //result = DES.decrypt(result) ; //System.out.println("完成:" + JSONObject.parseObject(result) + "\n"); return result ; } catch (Exception e) { e.printStackTrace() ; } finally { httpclient.close(); } return null ; } /** * 發起post請求,請求參數以Map集合形式傳入,封裝到List <NameValuePair> 發起post請求 * @param httpUrl * @param params * @return * @throws Exception */ public static String post(String httpUrl, Map<String, String> params) throws Exception { String result = null ; CloseableHttpClient httpclient = createSSLClientDefault(); //httpclient. //httpclient. BufferedReader in = null ; HttpPost httpPost = new HttpPost(httpUrl); httpPost.setConfig(requestConfig); List <NameValuePair> nvps = new ArrayList <NameValuePair>(); StringBuffer paramsBuf = new StringBuffer() ; for(Entry<String, String> e : params.entrySet()) { nvps.add(new BasicNameValuePair(e.getKey(), e.getValue())); paramsBuf.append("&").append(e.getKey()).append("=").append(e.getValue()) ; } httpPost.setEntity(new UrlEncodedFormEntity(nvps, "UTF-8")); try { // 報文參數27:&id=jn-3-767744&groupPlatProTerminalId=119667&extend=uwJZ8j3CkpGPL4rM5J6KJhjR99O7yAe3BAGLS8ooI8ijNqKHfzTaK6W9wQvjZEVOmWJ3HxFb2O9D // wDbe3++UiQ==&xxtCode=370000&terminalType=1&role=3&type=3 System.out.println("post請求報文地址:" + httpUrl+"?"+paramsBuf.toString()) ; CloseableHttpResponse response = httpclient.execute(httpPost); InputStream content = response.getEntity().getContent() ; in = new BufferedReader(new InputStreamReader(content, "UTF-8")); // in = new BufferedReader(new InputStreamReader(content, "GBK")); // in = new BufferedReader(new InputStreamReader(content)); StringBuilder sb = new StringBuilder(); String line = "" ; while ((line = in.readLine()) != null) { sb.append(line); } result = sb.toString() ; System.out.println("響應報文:" + result) ; // 響應報文:{"ret":0,"msg":"成功","callbackurl":"https://edu.10086.cn/test-sso/login?service=http%3A%2F%2F112.35.7.169%3A9010%2Feducloud%2Flogin%2Flogin%3Ftype%3D3%26mode%3D1%26groupId%3D4000573%26provincePlatformId%3D54","accesstoken":"2467946a-bee9-4d8c-8cce-d30181073b75"}Í //記錄報文日志 YdEduDao dao = SpringUtil.getBean(YdEduDao.class); dao.saveCallLog(httpUrl, paramsBuf.toString(), result); // HJY_SERVICE_LOG return result ; } catch (Exception e) { e.printStackTrace() ; } finally { httpclient.close(); } return null ; } public static JSONObject postData(String httpUrl, Object obj) throws Exception { JSONObject json = null; try{ String result = post(httpUrl,obj); json = JSONObject.parseObject(result); }catch(TimeoutException e){ System.out.println("請求超時了:"+httpUrl); throw e; }finally { return json ; } } public static String post(String httpUrl, Object obj) throws Exception { Map<String, String> params = getParamData(obj); String result = null ; try { result = post(httpUrl,params); return result ; } catch (Exception e) { e.printStackTrace() ; } finally { httpclient.close(); } return null ; } private static Map<String, String> getParamData(Object obj) { Class cla = obj.getClass(); Map<String, String> map = new HashMap<String, String>(); Method[] methods = cla.getDeclaredMethods(); try { for (Method m : methods) { String mname = m.getName(); if (mname.startsWith("get")) { String name = mname.substring(3, mname.length());// 截取字段 name = name.substring(0, 1).toLowerCase() + name.substring(1, name.length());// 把首字母變小寫 String value = m.invoke(obj)==null?"":m.invoke(obj).toString(); if(cla.equals(YdOrderRelationshipInfoSyncItem.class)&&name.equals("unionId")&&(value==null||value.equals(""))){ continue; } map.put(name,value);// 取值 } } Class superclass = cla.getSuperclass(); while (!superclass.equals(Object.class)) { Method[] superclassmethods = superclass.getDeclaredMethods(); for (Method m : superclassmethods) { String mname = m.getName(); if (mname.startsWith("get")) { String name = mname.substring(3, mname.length());// 截取字段 name = name.substring(0, 1).toLowerCase() + name.substring(1, name.length());// 把首字母變小寫 String value = m.invoke(obj)==null?"":m.invoke(obj).toString(); if((cla.equals(OrderInfoSyncItem.class)||cla.equals(AccountPasswordInfoSyncItem.class)||cla.equals(YdOrderRelationshipInfoSyncItem.class))&&name.equals("operation")) continue; map.put(name,value);// 取值 } } superclass = superclass.getSuperclass(); } } catch (Exception e) { e.printStackTrace(); } return map; }
public static CloseableHttpClient createSSLClientDefault(){ try { //SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { // 在JSSE中,證書信任管理器類就是實現了接口X509TrustManager的類。我們可以自己實現該接口,讓它信任我們指定的證書。 // 創建SSLContext對象,並使用我們指定的信任管理器初始化 //信任所有 X509TrustManager x509mgr = new X509TrustManager() { // 該方法檢查客戶端的證書,若不信任該證書則拋出異常 public void checkClientTrusted(X509Certificate[] xcs, String string) { } // 該方法檢查服務端的證書,若不信任該證書則拋出異常 public void checkServerTrusted(X509Certificate[] xcs, String string) { } // 返回受信任的X509證書數組。 public X509Certificate[] getAcceptedIssuers() { return null; } }; SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[] { x509mgr }, null); ////創建HttpsURLConnection對象,並設置其SSLSocketFactory對象 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); // HttpsURLConnection對象就可以正常連接HTTPS了,無論其證書是否經權威機構的驗證,只要實現了接口X509TrustManager的類MyX509TrustManager信任該證書。 return HttpClients.custom().setSSLSocketFactory(sslsf).build(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } // 創建默認的httpClient實例. return HttpClients.createDefault(); } }
另外,在針對http進行升級時,在HTTPS的證書未經權威機構認證的情況下,訪問HTTPS站點的兩種方法,一種方法是把該證書導入到Java的TrustStore文件中,另一種是自己實現並覆蓋JSSE缺省的證書信任管理器類。兩種方法各有優缺點,第一種方法不會影響JSSE的安全性,但需要手工導入證書;第二種方法雖然不用手工導入證書,但需要小心使用,否則會帶來一些安全隱患。