package com.isoftstone.core.util; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; /** * https post發送工具 * * @author 超神之巔 * */ public class HttpsTool { private static final String LINE_SEPARATOR = System.getProperty("line.separator"); static HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String urlHostName, SSLSession session) { System.out.println("Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost()); return true; } }; private static void trustAllHttpsCertificates() throws Exception { javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1]; javax.net.ssl.TrustManager tm = new miTM(); trustAllCerts[0] = tm; javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext .getInstance("SSL"); sc.init(null, trustAllCerts, null); javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sc .getSocketFactory()); } static class miTM implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public boolean isServerTrusted( java.security.cert.X509Certificate[] certs) { return true; } public boolean isClientTrusted( java.security.cert.X509Certificate[] certs) { return true; } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return; } } /** * 發送報文 * * 通信失敗時返回分兩種:一種是拋異常,一種是返回"",本方法取前者 * * @param requestData * 請求報文 * @param headMap * head請求頭參數 conn.setRequestProperty(key,value) * @param urlStr * 請求地址 * @param sendEncoding * 請求編碼 * @param recvEncoding * 返回編碼 * @param connectionTimeout * 鏈接超時時間 1000代表 1秒 * @param readTimeout * 讀取超時時間 1000代表1秒 * @return * @throws IOException * @author 超神之巔 */ public static String send(String requestData,Map<String,String> headMap, String urlStr, String sendEncoding, String recvEncoding, int connectionTimeout, int readTimeout,String contentType) throws Exception { URL url = null; HttpsURLConnection conn = null; ByteArrayOutputStream byteOut = null; BufferedReader readInfo = null; StringBuilder retBuilder = new StringBuilder();// 這里用不着多線程安全的StringBuffer OutputStream out = null; String line = null; StringBuilder reqDetailProcedure = new StringBuilder();// 這里用不着多線程安全的StringBuffer Date startTime = new Date(); reqDetailProcedure.append("請求時間:【" + DATE_FORMATER.format(startTime) + "】").append("\r\n"); reqDetailProcedure.append("請求地址:【" + urlStr + "】").append("\r\n"); reqDetailProcedure.append("真實請求方式及編碼:【post " + sendEncoding + "】").append("\r\n"); reqDetailProcedure.append("期望返回編碼:【" + recvEncoding + "】").append("\r\n"); reqDetailProcedure.append("請求超時時間:【" + readTimeout / 1000 + "s】").append("\r\n"); reqDetailProcedure.append("請求報文:【" + requestData + "】").append("\r\n"); try { // 如有必要,給?后的參數加encode(xxx,"UTF-8"),不然目標系統可能收到的request.getParameter()是亂碼 // String arg = java.net.URLEncoder.encode("中國","UTF-8"); // url = new URL(urlStr+"?deptname="+arg); url = new URL(urlStr); trustAllHttpsCertificates(); HttpsURLConnection.setDefaultHostnameVerifier(hv); conn = (HttpsURLConnection) url.openConnection(); conn.setRequestMethod("POST"); // 新增部分 //conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); conn.setRequestProperty("SOAPAction", "\"\""); conn.setRequestProperty("Accept", "application/xml, application/json,application/dime, multipart/related, text/*"); // 如果沒有下面這一行代碼,服務器端可以通過request.getParameter()和request.getInputStream()都接收到相同信息 // Content-Type相關知識鏈接: http://www.cnblogs.com/whatlonelytear/p/6187575.html // conn如果不設Content-Type,則默認成application/x-www-form-urlencoded // 當然也可以配置成application/x-www-form-urlencoded;charset=UTF-8,此時要求服務端返回UTF-8編碼,如果本地還用gbk去解碼,有可能得到亂碼 conn.setRequestProperty("Content-Type", contentType); // 如果把Content-Type的類型改變成非application/x-www-form-urlencoded,如改成text/xml, // 則目標服務器端僅能通過request.getInputStream()接收信息, 如conn.setRequestProperty("Content-Type", "text/xml;charset=GBK"); // Content-Type各類型解釋: http://blog.csdn.net/blueheart20/article/details/45174399 // conn.setRequestProperty("Accept-Charset", "utf-8");//未知 //Cache-Control說明鏈接:http://huangyunbin.iteye.com/blog/1943310 或 http://www.cnblogs.com/whatlonelytear/articles/6385390.html conn.setRequestProperty("Cache-Control", "no-cache"); //設置head頭,如果已存在,則覆蓋之前的head配置 if(headMap != null){ for(Map.Entry<String, String > entry : headMap.entrySet()){ conn.setRequestProperty(entry.getKey(),entry.getValue()); } } // conn.setRequestProperty("appName", appName);//各系統需要設置應用系統名,如電銷為telesales(無意義,本人自定義) conn.setUseCaches(false); // 忽略緩存 // get請求用不到conn.getOutputStream(),因為參數直接追加在地址后面,因此默認是false. // post請求(比如:文件上傳)需要往服務區傳輸大量的數據,這些數據是放在http的body里面的,因此需要在建立連接以后,往服務端寫數據. // 因為總是使用conn.getInputStream()獲取服務端的響應,因此默認值是true. conn.setDoOutput(true); // 使用 URL // 連接進行輸出,允許使用conn.getOutputStream().write() conn.setDoInput(true); // 使用 URL // 連接進行輸入,允許使用conn.getInputStream().read(); conn.setConnectTimeout(connectionTimeout);// 鏈接超時 conn.setReadTimeout(readTimeout);// 讀取超時 conn.connect();// 建立鏈接 byteOut = new ByteArrayOutputStream(); byteOut.write(requestData.getBytes(sendEncoding));// 以指定編碼發送,如果有亂碼,修改之 byte[] buf = byteOut.toByteArray(); out = conn.getOutputStream(); out.write(buf); out.flush(); reqDetailProcedure.append("響應碼和狀態:【" + conn.getResponseCode() + "/" + conn.getResponseMessage() + "】").append("\r\n"); if (HttpURLConnection.HTTP_OK == conn.getResponseCode()) {// 正確返回 InputStream tempStream = conn.getInputStream(); readInfo = new BufferedReader(new java.io.InputStreamReader(tempStream, recvEncoding));// 以指定編碼讀取返回信息,如果有亂碼,修改之 while ((line = readInfo.readLine()) != null) { retBuilder.append(line).append(LINE_SEPARATOR); } } else {// 沒有正確返回 // 這個else分支有冗余代碼,一來代碼不多,二來這個分支是經過長時間思考,還是保留了下來,方便我以后對錯誤做不拋異常處理等 InputStream tempStream = conn.getInputStream();// 如果響應碼不是200,一般在這里就開始報異常,不會走到下面幾行中去的. readInfo = new BufferedReader(new java.io.InputStreamReader(tempStream, recvEncoding));// cannotReach while ((line = readInfo.readLine()) != null) {// cannotReach retBuilder.append(line);// cannotReach }// cannotReach reqDetailProcedure.append("@@返回異常報文:【" + retBuilder + "】").append("\r\n");// cannotReach } } catch (IOException e) { e.printStackTrace(); throw e; } finally { // 目標服務端用response.setContentType("text/html;charset=UTF-8");然后源調用方可以用conn.getContentType()獲取到[猜測,暫未測試] // conn.getContentType()包含在Head返回的內容中. // 返回如果帶編碼,可以做成動態編碼去把流轉成字符串,暫不優化.這是極好的l優化點. reqDetailProcedure.append("返回內容類型及編碼:【" + conn.getContentType() + "】").append("\r\n"); reqDetailProcedure.append("返回HEAD頭信息:【" + conn.getHeaderFields() + "】").append("\r\n"); reqDetailProcedure.append("返回報文:【" + retBuilder.toString() + "】").append("\r\n"); Date endTime = new Date(); reqDetailProcedure.append("返回時間:【" + DATE_FORMATER.format(endTime) + "】").append("\r\n"); long diffMilliSecond = endTime.getTime() - startTime.getTime(); long diffSecond = (diffMilliSecond / 1000); reqDetailProcedure.append("耗時:【" + diffMilliSecond + "】毫秒,大約" + "【" + diffSecond + "】秒").append("\r\n"); System.out.println(reqDetailProcedure.toString()); //ThreadLocalListContainer.put(reqDetailProcedure.toString()); // ThreadLocalMapContainer.put(KingConstants.REQ_DETAIL_PROCEDURE,reqDetailProcedure.toString()) try { if (readInfo != null) { readInfo.close(); } if (byteOut != null) { byteOut.close(); } if (out != null) { out.close(); } if (conn != null) { conn.disconnect(); } } catch (Exception e) { System.out.println("關閉鏈接出錯!" + e.getMessage()); } } return retBuilder.toString(); } /** * 發送報文 * * 通信失敗時返回分兩種:一種是拋異常,一種是返回"",本方法取前者 * * @param requestData * 請求報文 * @param urlStr * 請求地址 * @param sendEncoding * 請求編碼 * @param recvEncoding * 返回編碼 * @param connectionTimeout * 鏈接超時時間 1000代表 1秒 * @param readTimeout * 讀取超時時間 1000代表1秒 * @return * @throws IOException * @author 超神之巔 */ public static String send(String requestData, String urlStr, String sendEncoding, String recvEncoding, int connectionTimeout, int readTimeout) throws Exception { boolean flag = isJson(requestData);//是否JSON String contentType="application/soap+xml"; if(flag) { contentType = "application/json"; } return send( requestData,null, urlStr, sendEncoding, recvEncoding, connectionTimeout, readTimeout,contentType); } /** * * @param filePath * 文件絕對路徑 * @param encoding * 讀取文件的編碼 * @return * @author 超神之巔 * @throws Exception */ public static String readStringFromFile(String filePath, String encoding) { File file = new File(filePath); // System.out.println("文件 "+filePath+"存在與否?: "+ file.exists()); String tempLine = null; String retStr = ""; InputStreamReader isr = null;// way1: // FileReader fr = null;//way2 StringBuilder sb = new StringBuilder(); try { if (file.exists()) { isr = new InputStreamReader(new FileInputStream(file), encoding);// way1: // fr = new FileReader(file);//way2 BufferedReader br = new BufferedReader(isr);// way1: // BufferedReader br = new BufferedReader(fr);;//way2: tempLine = br.readLine(); while (tempLine != null) { sb.append(tempLine).append(System.getProperty("line.separator")); tempLine = br.readLine(); } retStr = sb.toString(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (isr != null) isr.close(); } catch (Exception e) { e.printStackTrace(); } } // System.out.println("讀到的文件內容如下:"); // System.out.println(retStr); return retStr; } /** * 判斷字符串是否為JSON格式 * @param str * @return */ public static boolean isJson(String str){ if (StringUtils.isBlank(str)) { return false; } boolean flag1 = (str.startsWith("{") && str.endsWith("}")); boolean flag2 = (str.startsWith("[") && str.endsWith("]")); return (flag1 || flag2); } /*public static final void writeInfo(HttpServletResponse resp, String errorCode, String respInfo, Map<String, Object> map) { map.put("errorCode", errorCode); map.put("respInfo", respInfo); String detail = ThreadLocalListContainer.getAll(); map.put(KingConstants.DETAIL_PROCEDURE, detail); ThreadLocalListContainer.clearAll(); // map.put(KingConstants.DETAIL_PROCEDURE, detail); try { // String gsonInfo = new Gson().toJson(map);//old Gson gson = new GsonBuilder().setPrettyPrinting().create();//new String gsonInfo = gson.toJson(map); PrintWriter pw = resp.getWriter(); pw.print(gsonInfo); pw.flush(); pw.close(); } catch (IOException e) { e.printStackTrace(); } }*/ /** * * @param resp * @param errorCode * @param respInfo * @param map * ...... * @time 2017年4月23日 上午9:12:43 * @author 超神之巔 */ /*public static final void writeInfo(HttpServletResponse resp, KingResp result) { String detail = ThreadLocalListContainer.getAll(); result.setDetailProcedure(detail); ThreadLocalListContainer.clearAll(); try { // String gsonInfo = new Gson().toJson(map);//old Gson gson = new GsonBuilder().setPrettyPrinting().create();//new String gsonInfo = gson.toJson(result); FileTool.writeStringToFile("d:/temp/myinfo.txt", gsonInfo, "gbk", false); PrintWriter pw = resp.getWriter(); pw.print(gsonInfo); pw.flush(); pw.close(); } catch (IOException e) { e.printStackTrace(); } }*/ public static final void writeInfo(HttpServletResponse resp, String respInfo) { try { PrintWriter pw = resp.getWriter(); pw.print(respInfo); pw.flush(); pw.close(); } catch (IOException e) { e.printStackTrace(); } } public static final SimpleDateFormat DATE_FORMATER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss"); public static void main(String[] args) throws Exception { String requestUrl = "https://bx.zhcgs.gov.cn:8060/NewBxInfo/BxInfo.asmx"; //把cacerts20181015文到該目錄 System.setProperty("javax.net.ssl.trustStore", "K:\\myprogram\\jdk\\jdk8\\jdk1.8.0_141\\jre\\lib\\security\\cacerts20181015"); String requestData = readStringFromFile("d:/file/send.xml", "UTF-8");// 有亂碼,請修改指定編碼 String sencXml = HttpsTool.send(requestData, requestUrl, "utf-8", "utf-8", 300 * 1000, 300 * 1000);// 大家最終只要使用這一句代碼就可調用 } }