用Java發送web請求所用到的包都在java.net
下,在具體使用時可以用如下代碼,你可以把它封裝成一個工具類
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;public class Testt {/*HostnameVerifier 是一個Java接口其中的verify方法用來在下面的類中提供主機名的SSL校驗這里個人用不做具體校驗所有的主機名都給過*/final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier(){public boolean verify(String arg0, SSLSession arg1) {return true;}};/*trustAllHosts方法 方法創建了一個 TrustManager 接口的匿名實現它是用來檢查證書的,無論是服務器證書還是客戶端證書這通過重寫 checkServerTrusted 和 checkClientTrusted 方法為空 不做任何檢查*/public static void trustAllHosts() {TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager(){public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[] {};}public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}}};/*SSLContext 使用上述的 TrustManager 初始化一個 SSLContext 實例并將其設置為 HttpsURLConnection 的默認 SSLSocketFactory這允許 HttpsURLConnection 信任所有SSL證書TLS是握手協議的一種*/try {SSLContext sc = SSLContext.getInstance("TLS");sc.init(null, trustAllCerts, new SecureRandom());HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());}catch (Exception e) {e.printStackTrace();}}/*** 發出請求的具體方法* @param urlPath https協議接口的路徑 https://xxxxxx* @param Json 發出請求需要攜帶的參數,需要是Json格式* @return*/public static String doPostToJson(String urlPath, String Json) {String result = "";BufferedReader reader = null;HttpURLConnection conn = null;try {/*掉用上面的靜態方法,使得請求發出時,作為調用者的我們對服務端所擁有的任何https證書都是兼容的這里說一句題外話:一般情況下,作為接口使用者trustAllHosts方法里面的代碼是不變的,因為你作為使用者總沒道理說讓人家服務端兼容你什么,除非你是證書的使用者,不過這種情況會有證書廠商告訴你如何做,不得不說廠商服務是個好東西*/trustAllHosts();URL url = new URL(urlPath);/*如果協議是 https,則使用 HttpsURLConnection 并設置 HostnameVerifier 為 DO_NOT_VERIFY。否則,使用 HttpURLConnection*/if (url.getProtocol().toLowerCase().equals("https")) {HttpsURLConnection httpsConn = (HttpsURLConnection) url.openConnection();httpsConn.setHostnameVerifier(DO_NOT_VERIFY);conn = httpsConn;}else {conn = (HttpURLConnection) url.openConnection();}// 設置請求以POST形式發出conn.setRequestMethod("POST");// 是否攜帶請求提,POST和GET請求必須為trueconn.setDoOutput(true);// 是否會獲取服務端的響應,默認是trueconn.setDoInput(true);//禁用緩存conn.setUseCaches(false);// Keep-Alive告訴服務端對已有的TCP連接保持一定時間的活性,以保障后續發出的請求不需要建立新的TCP連接,從而減少網絡開銷conn.setRequestProperty("Connection", "Keep-Alive");//字符集,這個配置不是標準的請求頭,但是有些妖孽系統會讀取這個conn.setRequestProperty("Charset", "UTF-8");// 這個是請求格式,告訴服務端這個請求發送的是Json數據以及字符集conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");// 告訴服務端,相應格式也希望是json格式conn.setRequestProperty("accept", "application/json");//如果參數不為空則將參數以字節數流的方式寫入請求體里面if (Json != null) {byte[] writebytes = Json.getBytes();conn.setRequestProperty("Content-Length", String.valueOf(writebytes.length));OutputStream outwritestream = conn.getOutputStream();outwritestream.write(Json.getBytes());outwritestream.flush();outwritestream.close();}//讀取響應,相應數據也是一個jsonif (conn.getResponseCode() == 200) {reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));result = reader.readLine();}}catch (Exception e) {e.printStackTrace();}finally {//讀取相應用的輸入流需要在這里關閉,而寫入請求參數的輸出流是隨用隨關的if (reader != null) {try {reader.close();conn.disconnect();}catch (IOException e) {e.printStackTrace();}}}return result;}}
對于上面的代碼,重點要說明的第一點是HostnameVerifier
接口提供的方法,它主要用于訪問https協議下的接口時,驗證當前已和你建立連接的hostname(主機名)和連接的SSLSession(SSL會話)證書是否匹配,防止中間人攻擊,比如可以如下操作
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.URL;public class HostnameVerifierExample {public static void main(String[] args) {try {// 創建一個URL對象URL url = new URL("https://xxxxx");// 打開HTTPS連接HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();// 設置自定義的HostnameVerifierHostnameVerifier customVerifier = new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {// 在這里實現你的驗證邏輯// 例如,你可以比較hostname和session中的證書信息// 為了簡單起見,這里我們直接接受任何主機名(不推薦這樣做,有安全風險)// 在實際應用中,你應該根據你的安全策略來實現這個邏輯// 比如,你可以檢查hostname是否與你預期的服務器主機名匹配// 或者,你可以使用HttpsURLConnection.getDefaultHostnameVerifier()來獲取默認的驗證器,并基于它的結果進行擴展// 這是一個不安全的示例,僅用于說明如何使用verify方法// 在實際應用中,你應該避免這樣做return true; // 更安全的做法可能是這樣的:// if ("expected.hostname".equals(hostname)) {// return true;// } else {// HostnameVerifier defaultVerifier = HttpsURLConnection.getDefaultHostnameVerifier();// return defaultVerifier.verify(hostname, session);// }}};// 設置自定義的HostnameVerifier到連接中connection.setHostnameVerifier(customVerifier);// 繼續處理HTTPS連接,比如讀取響應等// ...// 關閉連接connection.disconnect();} catch (IOException e) {e.printStackTrace();}}
}
第二是trustAllHosts
方法里面的東西,TrustManager
是用來實現證書驗證的類,X509TrustManager
是X.509 證書的驗證實現,通俗的講是一個基礎的證書,因此我上面的代碼中也說了,如果你是正式的https證書使用者,會有廠商服務告訴你怎么驗證證書
,在X509TrustManager
中提供了三個方法
getAcceptedIssuers():返回此信任管理器接受的發行者證書(根證書或中間證書)數組。在上面的代碼中,這個方法返回了一個空數組,意味著它不做任何特定的發行者證書獲取checkServerTrusted(X509Certificate[] certs, String authType):驗證服務器提供的證書鏈是否可信。在代碼中,這個方法被空實現了,意味著不會進行任何驗證。checkClientTrusted(X509Certificate[] certs, String authType):驗證客戶端提供的證書鏈是否可信(在需要客戶端證書的情況下)。同樣,在代碼中,這個方法也被空實現了