近期要写一个Rest Client访问HTTPS协议的URL,用Post方式以JSON格式拿回URL的数据。最开始选择的Client是javax.ws.rs-2.0.jar这个jar包,Client的创建方式是:
Client client = ClientBuilder.newClient();
遇到的第一个问题就是证书不通过,具体如下:
SEVERE: Error while committing the request output stream.
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
网上折腾了半天,想到换一种Client的创建方式,如下:
Client client = ClientBuilder.newBuilder().hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { // TODO Auto-generated method stub return true; } }).build();这样之后,出现一个新的问题,如下:
SEVERE: Error while committing the request output stream.
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
这个问题,网上看了很久,有的说缺少指定hostname的SSL证书,用代码生成一个,(http://blog.csdn.net/faye0412/article/details/6883879)并把这个证书放到$JAVA_HOME$/jre/lib/security/文件夹下,我倒腾了一会也没能正常生成这个证书,于是放弃这个方法。
最后,想到是不是Client不对,于是换成HttpClient,没想到结果是一样的。最后终于找到了解决办法,共享一下:
使用Jersey REST客户端调用REST Web服务,主要包括三个jar包:jersey-client-1.18.jar,jersey-core-1.18.jar和jersy-json-q.q8.jar来生成Client,来实现带不可信SSL证书的Rest API服务,具体实现如下:
package restsample; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.X509Certificate; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import javax.ws.rs.core.MediaType; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.config.ClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.client.urlconnection.HTTPSProperties; public class RestSnow { public static ClientConfig init() throws NoSuchAlgorithmException, KeyManagementException { SSLContext context = SSLContext.getInstance("SSL"); TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] certs, String authType) {} @Override public void checkServerTrusted(X509Certificate[] certs, String authType) {} } }; context.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory()); ClientConfig config = new DefaultClientConfig(); config.getProperties().put( HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(new HostnameVerifier() { public boolean verify(String s, SSLSession sslSession) { return true; } }, context) ); return config; } public static void main(String[] args) { try { ClientConfig config = init(); Client client = Client.create(config); client.setFollowRedirects(true); WebResource resource = client.resource("https://65.50.203.159/api/data?auth_token=XzRso6mnz5FoKy2bYhNY"); resource.accept(MediaType.APPLICATION_JSON_TYPE); String postContent = "{\"dataSource\": \"rb_flow\",\"granularity\":{\"type\": \"period\",\"period\": \"pt5m\",\"timeZone\": \"America/Los_Angeles\",\"origin\": \"2014-04-05T12:00:00.000+08:00\"},\"intervals\":[\"2014-04-05T13:01:00+08:00/2014-04-06T13:01:00+08:00\"],\"queryType\": \"timeseries\",\"aggregations\":[{\"type\": \"dimCardinality\",\"name\": \"result\",\"fieldName\": \"ap_mac\"}]}"; // Post 方式 ClientResponse response = resource.type(MediaType.APPLICATION_JSON_TYPE). post(ClientResponse.class, con); // 将返回结果写入到本地文件中 // System.out.println("Output : \n" + response.getEntity(String.class)); // System.out.println(response.toString()); // System.out.println(response.getEntityInputStream()); InputStream inputStream = response.getEntityInputStream(); String fileName = "result.txt"; File f = new File("C:/"+fileName); FileOutputStream fos = new FileOutputStream(f); System.out.println("复制文件:" + inputStream.available() + "字节"); byte[] buffer = new byte[1024]; while(true) { if(inputStream.available() < 1024) { int c = -1; while((c = inputStream.read()) != -1) { fos.write(c); } break; } else { // 从来源文件读取数据至缓冲区 inputStream.read(buffer); // 将数组数据写入目的文件 fos.write(buffer); } } inputStream.close(); fos.close(); } catch(NoSuchAlgorithmException e) { e.printStackTrace(); } catch (KeyManagementException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }在此供参考,以后遇到相同的问题再回顾。