Java调用Http/Https接口(2)--HttpURLConnection/HttpsURLConnection调用Http/Https接口
HttpURLConnection是JDK自身提供的网络类,不需要引入额外的jar包。文中所使用到的软件版本:Java 1.8.0_191。
1、服务端
2、调用Http接口
2.1、GET请求
public static void get() { try { String requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=" + URLEncoder.encode("李白", "utf-8"); URL url = new URL(requestPath); //设置代理 //InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 8888); //Proxy proxy = new Proxy(Proxy.Type.HTTP, addr); //connection = (HttpURLConnection)url.openConnection(proxy); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("GET返回结果:" + back); } else { System.out.println("GET请求状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } }
2.2、POST请求(发送键值对数据)
public static void post() { try { String requestPath = "http://localhost:8080/demo/httptest/getUser"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.connect(); String param = "userId=1000&userName=李白"; bytesToOutputStream(param.getBytes(), connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("POST返回结果:" + back); } else { System.out.println("POST返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } }
2.3、POST请求(发送JSON数据)
public static void post2() { try { String requestPath = "http://localhost:8080/demo/httptest/addUser"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setRequestProperty("Content-type", "application/json"); connection.connect(); String param = "{\"userId\": \"1001\",\"userName\":\"杜甫\"}"; bytesToOutputStream(param.getBytes(), connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("POST2返回结果:" + back); } else { System.out.println("POST2返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } }
2.4、上传文件
public static void upload() { try { String requestPath = "http://localhost:8080/demo/httptest/upload"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setRequestProperty("Content-type", "file/*"); connection.connect(); FileInputStream fileInputStream = new FileInputStream("d:/a.jpg"); inputStreamToOutputStream(fileInputStream, connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("upload返回结果:" + back); } else { System.out.println("upload返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } }
2.5、上传文件及发送键值对数据
2.5.1、分析数据结构
通过抓包工具分析页面表单上传文件的过程,可以看出传输数据的结构:
2.5.2、根据分析出的数据结构编写代码
public static void multi() { String BOUNDARY = java.util.UUID.randomUUID().toString(); String TWO_HYPHENS = "--"; String LINE_END = "\r\n"; FileInputStream in = null; try { String requestPath = "http://localhost:8080/demo/httptest/multi"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestProperty("Content-type", "multipart/form-data; BOUNDARY=" + BOUNDARY); connection.connect(); StringBuffer sb = new StringBuffer(); //封装键值对数据1 sb.append(TWO_HYPHENS).append(BOUNDARY).append(LINE_END); sb.append("Content-Disposition: form-data; name=\"param1\"").append(LINE_END); sb.append(LINE_END); sb.append("参数1").append(LINE_END); //封装键值对数据2 sb.append(TWO_HYPHENS).append(BOUNDARY).append(LINE_END); sb.append("Content-Disposition: form-data; name=\"param2\"").append(LINE_END); sb.append(LINE_END); sb.append("参数2").append(LINE_END); //封装文件数据 sb.append(TWO_HYPHENS).append(BOUNDARY).append(LINE_END); sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"a.jpg\"").append(LINE_END); sb.append("Content-Type: file/*").append(LINE_END); sb.append(LINE_END); bytesToOutputStream(sb.toString().getBytes(), connection.getOutputStream()); in = new FileInputStream("d:/a.jpg"); inputStreamToOutputStream(in, connection.getOutputStream()); //写入标记结束位 String end = (LINE_END + TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + LINE_END); bytesToOutputStream(end.getBytes(), connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("multi返回结果:" + back); } else { System.out.println("multi返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } finally { FileUtil.close(in); } }
2.5.3、上传文件方法变通
这种方式上传文件完全是模拟页面的提交行为来编码的,比较繁琐。可以把文件转成字符串,然后通过键值对传给服务端,服务端执行相反的过程来存储文件:
客户端:文件-> 字节数组->Base64字符串
服务端:Base64字符串-> 字节数组->文件
按照这种方式来实现应该比较容易,这里就不演示了。
2.6、下载文件
public static void download() { FileOutputStream out = null; try { String requestPath = "http://localhost:8080/demo/httptest/download"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.connect(); System.out.println("download返回状态码:" + connection.getResponseCode()); String contentDisposition = connection.getHeaderField("Content-Disposition");//attachment; filename="a.jpg" String fileName = "none"; if (contentDisposition != null && contentDisposition.indexOf("filename") > -1) { fileName = contentDisposition.substring(contentDisposition.indexOf("\"") + 1, contentDisposition.length() - 1); fileName = java.net.URLDecoder.decode(fileName, "utf-8"); } out = new FileOutputStream("d:/temp/dowload_" + System.currentTimeMillis() + "_" + fileName); inputStreamToOutputStream(connection.getInputStream(), out); } catch (Exception e) { e.printStackTrace(); } finally { FileUtil.close(out); } }
2.7、完整例子
package com.abc.demo.http.client; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; //import java.net.InetSocketAddress; //import java.net.Proxy; import java.net.URL; import java.net.URLEncoder; import com.abc.demo.common.util.FileUtil; /** * * 通过HttpURLConnection调用Http接口 * */ public class HttpURLConnectionCase { /** * GET请求 */ public static void get() { try { String requestPath = "http://localhost:8080/demo/httptest/getUser?userId=1000&userName=" + URLEncoder.encode("李白", "utf-8"); URL url = new URL(requestPath); //设置代理 //InetSocketAddress addr = new InetSocketAddress("127.0.0.1", 8888); //Proxy proxy = new Proxy(Proxy.Type.HTTP, addr); //connection = (HttpURLConnection)url.openConnection(proxy); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); connection.connect(); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("GET返回结果:" + back); } else { System.out.println("GET请求状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } } /** * POST请求,发送键值对数据 */ public static void post() { try { String requestPath = "http://localhost:8080/demo/httptest/getUser"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.connect(); String param = "userId=1000&userName=李白"; bytesToOutputStream(param.getBytes(), connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("POST返回结果:" + back); } else { System.out.println("POST返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } } /** * POST请求,发送json格式数据 */ public static void post2() { try { String requestPath = "http://localhost:8080/demo/httptest/addUser"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setRequestProperty("Content-type", "application/json"); connection.connect(); String param = "{\"userId\": \"1001\",\"userName\":\"杜甫\"}"; bytesToOutputStream(param.getBytes(), connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("POST2返回结果:" + back); } else { System.out.println("POST2返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } } /** * 上传文件 */ public static void upload() { try { String requestPath = "http://localhost:8080/demo/httptest/upload"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setRequestProperty("Content-type", "file/*"); connection.connect(); FileInputStream fileInputStream = new FileInputStream("d:/a.jpg"); inputStreamToOutputStream(fileInputStream, connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("upload返回结果:" + back); } else { System.out.println("upload返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } } /** * 上传文件及发送键值对数据 */ public static void multi() { String BOUNDARY = java.util.UUID.randomUUID().toString(); String TWO_HYPHENS = "--"; String LINE_END = "\r\n"; FileInputStream in = null; try { String requestPath = "http://localhost:8080/demo/httptest/multi"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestProperty("Content-type", "multipart/form-data; BOUNDARY=" + BOUNDARY); connection.connect(); StringBuffer sb = new StringBuffer(); //封装键值对数据1 sb.append(TWO_HYPHENS).append(BOUNDARY).append(LINE_END); sb.append("Content-Disposition: form-data; name=\"param1\"").append(LINE_END); sb.append(LINE_END); sb.append("参数1").append(LINE_END); //封装键值对数据2 sb.append(TWO_HYPHENS).append(BOUNDARY).append(LINE_END); sb.append("Content-Disposition: form-data; name=\"param2\"").append(LINE_END); sb.append(LINE_END); sb.append("参数2").append(LINE_END); //封装文件数据 sb.append(TWO_HYPHENS).append(BOUNDARY).append(LINE_END); sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"a.jpg\"").append(LINE_END); sb.append("Content-Type: file/*").append(LINE_END); sb.append(LINE_END); bytesToOutputStream(sb.toString().getBytes(), connection.getOutputStream()); in = new FileInputStream("d:/a.jpg"); inputStreamToOutputStream(in, connection.getOutputStream()); //写入标记结束位 String end = (LINE_END + TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + LINE_END); bytesToOutputStream(end.getBytes(), connection.getOutputStream()); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) { byte[] b = getBytesFromInputStream(connection.getInputStream()); String back = new String(b); System.out.println("multi返回结果:" + back); } else { System.out.println("multi返回状态码:" + connection.getResponseCode()); } } catch (Exception e) { e.printStackTrace(); } finally { FileUtil.close(in); } } /** * 下载文件 */ public static void download() { FileOutputStream out = null; try { String requestPath = "http://localhost:8080/demo/httptest/download"; URL url = new URL(requestPath); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("POST"); connection.connect(); System.out.println("download返回状态码:" + connection.getResponseCode()); String contentDisposition = connection.getHeaderField("Content-Disposition");//attachment; filename="a.jpg" String fileName = "none"; if (contentDisposition != null && contentDisposition.indexOf("filename") > -1) { fileName = contentDisposition.substring(contentDisposition.indexOf("\"") + 1, contentDisposition.length() - 1); fileName = java.net.URLDecoder.decode(fileName, "utf-8"); } out = new FileOutputStream("d:/temp/dowload_" + System.currentTimeMillis() + "_" + fileName); inputStreamToOutputStream(connection.getInputStream(), out); } catch (Exception e) { e.printStackTrace(); } finally { FileUtil.close(out); } } /** * 从输入流获取数据 * @param in * @return * @throws IOException */ private static byte[] getBytesFromInputStream(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int len; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } byte[] bytes = out.toByteArray(); out.close(); return bytes; } /** * 把字节数组写入输出流 * @param bytes * @param out * @throws IOException */ private static void bytesToOutputStream(byte[] bytes, OutputStream out) throws IOException { ByteArrayInputStream in = new ByteArrayInputStream(bytes); byte[] b = new byte[1024]; int len; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } out.flush(); in.close(); } /** * 从输入流读取数据并写入输出流 * @param out * @param bytes * @throws IOException */ private static void inputStreamToOutputStream(InputStream in, OutputStream out) throws IOException { byte[] b = new byte[1024]; int len; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } out.flush(); } public static void main(String[] args) { get(); post(); post2(); upload(); multi(); download(); } }
3、调用Https接口
调用Https接口需要用HttpsURLConnection,与调用Http接口不一样的部分主要在设置ssl部分,下面用GET请求来演示ssl的设置,其他调用方式类似。
3.1、导入cer证书
对于自定义证书的网站,需把网站证书用 Java 自带的工具 keytool 导入本地信任密钥库,如导入到 d:/temp 目录下:
cd d:/temp
keytool -import -alias aa -keystore trust.keystore -file abc.cer -trustcacerts
也可以不导入,使用默认的实现 DefaultTrustManager,信任所有网站,将导致不安全。
3.2、示例
package com.abc.demo.http.client; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import com.abc.demo.common.util.FileUtil; /** * 通过HttpsURLConnection调用Https接口 */ public class HttpsURLConnectionCase { public static void main(String[] args) { try { /* * 请求有权威证书的地址 */ String requestPath = "https://www.baidu.com/"; HttpsURLConnection connection = getHttpsURLConnection(requestPath, "GET"); String result = new String(getBytesFromInputStream(connection.getInputStream())); System.out.println("GET1返回结果:" + result); /* * 请求自定义证书的地址 */ //获取信任库 KeyStore trustStore = getkeyStore("jks", "d:/temp/cacerts", "123456"); //不需客户端证书 requestPath = "https://x.x.x.x:9010/zsywservice"; connection = getHttpsURLConnection(requestPath, "GET", null, null, trustStore); result = new String(getBytesFromInputStream(connection.getInputStream())); System.out.println("GET2返回结果:" + result); //需客户端证书,客户端证书可以是用 keytool 生成的 pks12 格式密钥库,也可以是用 OpenSSL 生成的 pkcs12 格式证书。 requestPath = "https://x.x.x.x:9016/zsywservice"; KeyStore keyStore = getkeyStore("pkcs12", "d:/client.p12", "123456"); connection = getHttpsURLConnection(requestPath, "GET", keyStore, "123456", trustStore); result = new String(getBytesFromInputStream(connection.getInputStream())); System.out.println("GET3返回结果:" + result); } catch (Exception e) { e.printStackTrace(); } } /** * 获取证书 * @return */ private static KeyStore getkeyStore(String type, String filePath, String password) { KeyStore keySotre = null; FileInputStream in = null; try { keySotre = KeyStore.getInstance(type); in = new FileInputStream(new File(filePath)); keySotre.load(in, password.toCharArray()); } catch (Exception e) { e.printStackTrace(); } finally { FileUtil.close(in); } return keySotre; } private static HttpsURLConnection getHttpsURLConnection(String uri, String method) throws Exception { URL url = new URL(uri); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setRequestMethod(method); connection.setDoInput(true); connection.setDoOutput(true); return connection; } /** * 获取连接 * @param uri * @param method * @param keyStore * @param trustStore * @return * @throws Exception */ private static HttpsURLConnection getHttpsURLConnection(String uri, String method, KeyStore keyStore, String keyStorePassword, KeyStore trustStore) throws Exception { KeyManager[] keyManagers = null; TrustManager[] trustManagers = null; if (keyStore != null) { KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); keyManagerFactory.init(keyStore, keyStorePassword.toCharArray()); keyManagers = keyManagerFactory.getKeyManagers(); } if (trustStore != null) { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509"); trustManagerFactory.init(trustStore); trustManagers = trustManagerFactory.getTrustManagers(); } else { trustManagers = new TrustManager[] { new DefaultTrustManager()}; } //设置服务端支持的协议 SSLContext context = SSLContext.getInstance("TLSv1.2"); context.init(keyManagers, trustManagers, null); SSLSocketFactory sslFactory = context.getSocketFactory(); URL url = new URL(uri); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); connection.setSSLSocketFactory(sslFactory); //验证URL的主机名和服务器的标识主机名是否匹配 connection.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { //if ("xxx".equals(hostname)) { // return true; //} else { // return false; //} return true; } }); connection.setRequestMethod(method); connection.setDoInput(true); connection.setDoOutput(true); return connection; } /** * 默认的TrustManager实现,不安全 */ private static final class DefaultTrustManager implements X509TrustManager { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } } /** * 从输入流获取数据 * @param in * @return * @throws IOException */ private static byte[] getBytesFromInputStream(InputStream in) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] b = new byte[1024]; int len; while ((len = in.read(b)) != -1) { out.write(b, 0, len); } byte[] bytes = out.toByteArray(); out.close(); return bytes; } }
3.2、Received fatal alert:handshake_failure
出现这种这种问题的一种情况是由于客户端使用的协议,服务端不支持造成的;修改为服务端所支持的协议即可。在getHttpsURLConnection方法中修改:
SSLContext context = SSLContext.getInstance("TLSv1.2");
可选的协议参数有:SSL、SSLv2、SSLv3、TLS、TLSv1、TLSv1.1、TLSv1.2;TLS是SSL标准化的产物,故现在基本只使用TLS相关的参数。
可以用linux的curl命令来测试服务端支持哪些协议:
curl -k https://x.x.x.x:9016/zsywservice --tlsv1.2 -v
可选协议有:--tlsv1、--tlsv1.0、--tlsv1.1、--tlsv1.2、--tlsv1.3
如果出现如下信息,则表示不支持: