一、问题描述
通过java.net.URLConnection下载https请求链接,报“javax.net.ssl.SSLException: Received fatal alert: protocol_version”异常,详情错误日志>>
javax.net.ssl.SSLException: Received fatal alert: protocol_version@b@ at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)@b@ at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)@b@ at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1979)@b@ at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1086)@b@ at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332)@b@ at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359)@b@ at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343)@b@ at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)@b@ at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)@b@ at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:153)@b@ at com.xwood.craw.crawler.spider.core.DownLoadHandler.httpsExecute(DownLoadHandler.java:167)@b@ at com.xwood.craw.crawler.spider.core.DownLoadHandler.main(DownLoadHandler.java:243)
二、代码示例
1、上面报错代码示例(对于http链接资源可以正常获取数据,对于https资源就报SSLException异常错误)
package com.xwood.craw.crawler.spider.core;@b@@b@import java.io.BufferedInputStream;@b@import java.io.BufferedOutputStream;@b@import java.io.File;@b@import java.io.FileOutputStream;@b@import java.net.SocketTimeoutException;@b@import java.net.URL;@b@import java.net.URLConnection;@b@import java.util.HashMap;@b@import java.util.Map;@b@@b@public class DownLoadHandler extends DefaultHttp{@b@ @b@ @b@ public void httpsExecute(String eurl,String eoutpath){@b@ try {@b@ File f = new File(eoutpath);@b@ if (f.exists()){@b@ f.delete();@b@ }@b@ @b@ URL _url = new URL(eurl); @b@ try {@b@ URLConnection urlconn = _url.openConnection();@b@ urlconn.setConnectTimeout(30000);@b@ urlconn.connect();@b@ } catch (SocketTimeoutException ee) {@b@ return ;@b@ }@b@ @b@ BufferedInputStream bis = new BufferedInputStream(_url.openStream());@b@ if(eoutpath.lastIndexOf("/")!=-1){@b@ File _exists_file=new File(eoutpath.substring(0, eoutpath.lastIndexOf("/")));@b@ if(!_exists_file.exists())@b@ _exists_file.mkdirs();@b@ }@b@ @b@ FileOutputStream out=new FileOutputStream(eoutpath);@b@ BufferedOutputStream bos = new BufferedOutputStream(out); @b@ @b@ byte buf[] = new byte[10000]; @b@ int len; @b@ @b@ int isRefered=bis.read();@b@ if(isRefered==-1){@b@ Map<String,String> tmpHeaders=new HashMap<String,String>();@b@ tmpHeaders.put("Referer", eurl);@b@ this.setHeaders(tmpHeaders);@b@ try {@b@ bis = new BufferedInputStream(this.doGetStream(eurl,@b@ 30000));@b@ } catch (Exception e) {@b@ return;@b@ }@b@ } @b@ @b@ while((len=bis.read(buf)) != -1){ @b@ bos.write(buf, 0, len); @b@ } @b@ @b@ bos.close(); @b@ out.close(); @b@ bis.close(); @b@ } catch (Exception e) {@b@ e.printStackTrace();@b@ }@b@ }@b@ @b@ public static void main(String[] args){@b@ new DownLoadHandler().httpsExecute("https://repo.maven.apache.org/maven2/abbot/abbot/0.12.3/abbot-0.12.3.pom","C:/temp/apache-maven2/abbot-0.12.3.pom");@b@ }@b@@b@}
package com.xwood.craw.crawler.spider.core;@b@@b@import java.io.File;@b@import java.io.InputStream;@b@import java.util.ArrayList;@b@import java.util.List;@b@import java.util.Map;@b@import java.util.Set;@b@@b@import org.apache.commons.io.FileUtils;@b@import org.apache.http.HttpEntity;@b@import org.apache.http.HttpResponse;@b@import org.apache.http.NameValuePair;@b@import org.apache.http.client.HttpClient;@b@import org.apache.http.client.entity.UrlEncodedFormEntity;@b@import org.apache.http.client.methods.HttpGet;@b@import org.apache.http.client.methods.HttpPost;@b@import org.apache.http.cookie.Cookie;@b@import org.apache.http.entity.StringEntity;@b@import org.apache.http.impl.client.AbstractHttpClient;@b@import org.apache.http.impl.client.DefaultHttpClient;@b@import org.apache.http.message.BasicNameValuePair;@b@import org.apache.http.params.CoreConnectionPNames;@b@import org.apache.http.util.EntityUtils;@b@@b@@b@/**@b@ * 对HttpClient进行封装,默认的HTTP请求工具@b@ */@b@@SuppressWarnings("deprecation")@b@public class DefaultHttp {@b@ @b@ private static final String DEFAULT_CHARSET = "utf-8";@b@ //http字符集@b@ private String charset = DEFAULT_CHARSET;@b@ //请求参数@b@ private Map<String, String> params;@b@ //http包头@b@ private Map<String,String> headers;@b@ @b@ private HttpClient httpClient;@b@ @b@ private HttpResponse httpResponse;@b@ @b@ private InputStream httpStream;@b@ @b@ public DefaultHttp(){@b@ httpClient = new DefaultHttpClient(); @b@ }@b@ @b@ /**@b@ * 设置字符编码@b@ * @param charset@b@ * @return@b@ */@b@ public DefaultHttp setCharset(String charset){@b@ this.charset = (charset!=null && !"".equals(charset.trim()))?charset:DEFAULT_CHARSET;@b@ return this;@b@ }@b@ /**@b@ * 设置From Data参数@b@ * @param params@b@ * @return@b@ */@b@ public DefaultHttp setParams(Map<String, String> params){@b@ this.params = params;@b@ return this;@b@ }@b@ @b@ public DefaultHttp setHeaders(Map<String,String> headers){@b@ this.headers = headers;@b@ return this;@b@ }@b@ @b@ /**@b@ * 发送一个Post请求@b@ * @param reqUrl@b@ * @return@b@ * @throws Exception@b@ */@b@ public DefaultHttp doPost(String reqUrl) throws Exception{@b@ HttpPost httPost = new HttpPost(reqUrl);@b@ //设置from data@b@ if(params!=null && !params.isEmpty()){@b@ List<NameValuePair> nvps = new ArrayList<NameValuePair>();@b@ Set<String> keys = params.keySet();@b@ for(String key : keys){@b@ nvps.add(new BasicNameValuePair(key,params.get(key)));@b@ }@b@ httPost.setEntity(new UrlEncodedFormEntity(nvps, charset));@b@ }@b@ //设置header@b@ if(headers!=null && !headers.isEmpty()){@b@ Set<String> keys = headers.keySet();@b@ for(String key : keys){@b@ httPost.addHeader(key,headers.get(key));@b@ }@b@ }@b@ this.httpResponse=httpClient.execute(httPost);@b@ HttpEntity httpEntity= httpResponse.getEntity();@b@ httpStream = httpEntity.getContent();@b@ return this;@b@ }@b@ @b@ /**@b@ * 发送一个Get请求@b@ * @param reqUrl@b@ * @return@b@ * @throws Exception@b@ */@b@ public DefaultHttp doGet(String reqUrl) throws Exception{@b@ HttpGet httpGet = new HttpGet(reqUrl);@b@ //设置header@b@ if(headers!=null && !headers.isEmpty()){@b@ Set<String> keys = headers.keySet();@b@ for(String key : keys){@b@ httpGet.addHeader(key,headers.get(key));@b@ }@b@ }@b@ httpResponse=httpClient.execute(httpGet);@b@ HttpEntity httpEntity= httpResponse.getEntity();@b@ httpStream = httpEntity.getContent();@b@ return this;@b@ }@b@ @b@ public InputStream doGetStream(String reqUrl,int timeout) throws Exception{@b@ HttpGet httpGet = new HttpGet(reqUrl);@b@ //设置header@b@ if(headers!=null && !headers.isEmpty()){@b@ Set<String> keys = headers.keySet();@b@ for(String key : keys){@b@ httpGet.addHeader(key,headers.get(key));@b@ }@b@ }@b@ httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout); @b@ httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout); @b@ httpResponse=httpClient.execute(httpGet);@b@ @b@ HttpEntity httpEntity= httpResponse.getEntity();@b@ httpStream = httpEntity.getContent();@b@ return httpStream;@b@ }@b@ @b@ @b@ @b@ /**@b@ * 获取到html内容@b@ * @return@b@ * @throws Exception@b@ */@b@ public String asHtml() throws Exception{@b@ return EntityUtils.toString(httpResponse.getEntity(),charset).trim();@b@ }@b@ @b@ /**@b@ * 保存文件,保存文件必须确保在发起Http请求(执行doGet或者doPost方法)之后才能进行保存操作,@b@ * 否则保存文件不成功.@b@ * 注意:该方法不能够在asHtml之后调用,否则会抛出 Attempted read from closed stream.异常@b@ * @param file@b@ * @throws Exception@b@ */@b@ public void save(File file) throws Exception{@b@ if(httpStream == null) throw new Exception("还未执行HTTP请求,保存文件失败");@b@ FileUtils.copyInputStreamToFile(httpStream, file);@b@ }@b@ @b@ /**@b@ * 添加Cookie@b@ * @param cookie@b@ */@b@ public void addCookie(Cookie cookie){@b@ ((AbstractHttpClient)httpClient).getCookieStore().addCookie(cookie);@b@ }@b@ @b@ /**@b@ * 获取到Cookie信息@b@ * @return@b@ */@b@ public List<Cookie> getCookies(){@b@ return ((AbstractHttpClient)httpClient).getCookieStore().getCookies();@b@ }@b@ @b@ /**@b@ * 发送一个Post请求@b@ * @param reqUrl@b@ * @param xml@b@ * @return@b@ * @throws Exception@b@ */@b@ public DefaultHttp doXml(String reqUrl,String xml) throws Exception{@b@ HttpPost httPost = new HttpPost(reqUrl);@b@ StringEntity entity = new StringEntity(xml);@b@ httPost.setEntity(entity);@b@ //设置header@b@ if(headers!=null && !headers.isEmpty()){@b@ Set<String> keys = headers.keySet();@b@ for(String key : keys){@b@ httPost.addHeader(key,headers.get(key));@b@ }@b@ }@b@ this.httpResponse=httpClient.execute(httPost);@b@ HttpEntity httpEntity= httpResponse.getEntity();@b@ httpStream = httpEntity.getContent();@b@ return this;@b@ }@b@ @b@ /**@b@ * 获取到请求返回的状态码@b@ * @return@b@ */@b@ public int getResponseCode(){@b@ return this.httpResponse.getStatusLine().getStatusCode();@b@ }@b@ @b@ @b@}
2、通过下面定义SSLDownLoadHttpClient类,解决上面SSLException问题
package com.xwood.craw.crawler.spider.core;@b@@b@import java.io.IOException;@b@import java.security.KeyManagementException;@b@import java.security.NoSuchAlgorithmException;@b@@b@import javax.net.ssl.SSLContext;@b@@b@import org.apache.http.HttpResponse;@b@import org.apache.http.client.config.RequestConfig;@b@import org.apache.http.client.methods.HttpGet;@b@import org.apache.http.conn.ssl.SSLConnectionSocketFactory;@b@import org.apache.http.conn.ssl.SSLContexts;@b@import org.apache.http.impl.client.CloseableHttpClient;@b@import org.apache.http.impl.client.HttpClients;@b@import org.apache.http.util.EntityUtils;@b@@b@@b@public class SSLDownLoadHttpClient{@b@ @b@ @b@ public static String executePost(String uri, String requestBody, Integer timeOut) throws NoSuchAlgorithmException, KeyManagementException {@b@ @b@// SSLContext ctx= SSLContext.getInstance("TLSv1.2");@b@// ctx.init(null, null, null);@b@ SSLContext ctx = SSLContexts.custom().useProtocol("TLSv1.2").build();@b@ CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(new SSLConnectionSocketFactory(ctx)).build();@b@// HttpPost httpPost = new HttpPost(uri);@b@ HttpGet httpGet =new HttpGet(uri);@b@ String strResult = null;@b@ try {@b@ //httpPost.setEntity(new UrlEncodedFormEntity(parameters, Consts.UTF_8));@b@// HttpEntity httpEntity = new StringEntity(requestBody,ContentType.TEXT_XML);@b@// httpPost.setEntity(httpEntity);@b@ /*连接超时*/@b@ if (null != timeOut) {@b@ RequestConfig requestConfig = RequestConfig.custom()@b@ .setConnectTimeout(timeOut).setConnectionRequestTimeout(timeOut)@b@ .setSocketTimeout(timeOut).build();@b@// httpPost.setConfig(requestConfig);@b@ httpGet.setConfig(requestConfig);@b@ }@b@// HttpResponse httpResponse = httpClient.execute(httpPost);@b@ HttpResponse httpResponse = httpClient.execute(httpGet);@b@ if (httpResponse.getStatusLine().getStatusCode() == 200) {@b@ strResult = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");//获得返回的结果@b@ }@b@ } catch (IOException e) {@b@ e.printStackTrace();@b@ } finally {@b@ try {@b@ httpClient.close();//释放资源@b@ } catch (IOException e) {@b@ e.printStackTrace();@b@ }@b@ }@b@ System.out.println("===="+strResult);@b@ return strResult;@b@ }@b@ @b@ @b@ public static void main(String[] args) throws Exception{@b@ SSLDownLoadHttpClient.executePost("https://repo.maven.apache.org/maven2/abbot/abbot/0.12.3/abbot-0.12.3.pom","", 120000);@b@ }@b@@b@}
控制台打印结果
====<project>@b@ <modelVersion>4.0.0</modelVersion>@b@ <groupId>abbot</groupId>@b@ <artifactId>abbot</artifactId>@b@ <name>Abbot</name>@b@ <version>0.12.3</version>@b@</project>