参考:基于java的https双向认证,android上亦可用
Android Https相关完全解析 当OkHttp遇到Https
在android中,经常不可避免的的使用https和http访问网络数据,因此需要先构建一个网络访问client
其中https的访问需要使用SSLSocket,但对于一般安全性要求不是很高的通信,一般设置SSLSocket是允许所有主机通过,下面给出2中,一种是允许所有主机通过的https通信,另一种是加密传输,需要使用cert证书。
允许所有主机通过
public class GlobalUtils { public static HttpClient getAndroidHttpClient(int connTimeout, String userAgent,int retryTimes) { AbstractHttpClient httpClient = null; //设置请求控制参数 HttpParams params = new BasicHttpParams(); ConnManagerParams.setTimeout(params, connTimeout); HttpConnectionParams.setSoTimeout(params, connTimeout); HttpConnectionParams.setConnectionTimeout(params, connTimeout); if (TextUtils.isEmpty(userAgent)) { userAgent = System.getProperty("http.agents", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36"); } HttpProtocolParams.setUserAgent(params, userAgent); //设置最大的连接数 ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(10)); ConnManagerParams.setMaxTotalConnections(params, 10); HttpConnectionParams.setTcpNoDelay(params, true); //关闭Socket缓冲 HttpConnectionParams.setSocketBufferSize(params, 1024 * 8);//本方法与setTcpNoDelay冲突 HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); schemeRegistry.register(new Scheme("https", DefaultSSLSocketFactory.getSocketFactory(), 443)); httpClient = new DefaultHttpClient(new ThreadSafeClientConnManager(params, schemeRegistry), params); httpClient.setHttpRequestRetryHandler(new RetryHandler(retryTimes)); httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(org.apache.http.HttpRequest httpRequest, HttpContext httpContext) throws org.apache.http.HttpException, IOException { if (!httpRequest.containsHeader("Accept-Encoding")) { httpRequest.addHeader("Accept-Encoding", "gzip"); } } }); httpClient.addResponseInterceptor(new HttpResponseInterceptor() { @Override public void process(HttpResponse response, HttpContext httpContext) throws org.apache.http.HttpException, IOException { final HttpEntity entity = response.getEntity(); if (entity == null) { return; } final Header encoding = entity.getContentEncoding(); if (encoding != null) { for (HeaderElement element : encoding.getElements()) { if (element.getName().equalsIgnoreCase("gzip")) { response.setEntity(new GZipDecompressingEntity(response.getEntity())); return; } } } } }); return httpClient; } }
上面的重试RetryHandler是请求失败后重试的规则
public class RetryHandler implements HttpRequestRetryHandler { //需要实现HttpRequestRetryHandler private static final int RETRY_SLEEP_INTERVAL = 500; private static HashSet<Class<?>> exceptionWhiteList = new HashSet<Class<?>>(); private static HashSet<Class<?>> exceptionBlackList = new HashSet<Class<?>>(); static { exceptionWhiteList.add(NoHttpResponseException.class); exceptionWhiteList.add(UnknownHostException.class); exceptionWhiteList.add(SocketException.class); exceptionBlackList.add(InterruptedIOException.class); exceptionBlackList.add(SSLHandshakeException.class); } private final int maxRetries; public RetryHandler(int maxRetries) { this.maxRetries = maxRetries; } @Override public boolean retryRequest(IOException exception, int retriedTimes, HttpContext context) { boolean retry = true; if (exception == null || context == null) { return false; } Object isReqSent = context.getAttribute(ExecutionContext.HTTP_REQ_SENT); boolean sent = isReqSent == null ? false : (Boolean) isReqSent; if (retriedTimes > maxRetries) { retry = false; } else if (exceptionBlackList.contains(exception.getClass())) { retry = false; } else if (exceptionWhiteList.contains(exception.getClass())) { retry = true; } else if (!sent) { retry = true; } if (retry) { try { Object currRequest = context.getAttribute(ExecutionContext.HTTP_REQUEST); if (currRequest != null) { //这里只允许GET请求的重试,因为在一般访问中POST重试会造成重复提交问题,因此不宜使用 if (currRequest instanceof HttpRequestBase) { HttpRequestBase requestBase = (HttpRequestBase) currRequest; retry = "GET".equals(requestBase.getMethod()); } else if (currRequest instanceof RequestWrapper) { RequestWrapper requestWrapper = (RequestWrapper) currRequest; retry = "GET".equals(requestWrapper.getMethod()); } } else { retry = false; LogUtils.e("retry error, curr request is null"); } } catch (Throwable e) { retry = false; LogUtils.e("retry error", e); } } if (retry) { SystemClock.sleep(RETRY_SLEEP_INTERVAL); // sleep a while and retry http request again. } return retry; } }
需要重写SSLSocketFactory来支持所有主机通过
public class DefaultSSLSocketFactory extends SSLSocketFactory { //ssl上下文环境 private SSLContext sslContext = SSLContext.getInstance("TLS"); //证书保存对象 private static KeyStore trustStore; static { try { trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); //通常这里需要加载证书 trustStore.load(null, null); } catch (Throwable e) { e.printStackTrace(); } } private static DefaultSSLSocketFactory instance; public static DefaultSSLSocketFactory getSocketFactory() { if (instance == null) { try { instance = new DefaultSSLSocketFactory(); } catch (Throwable e) { LogUtils.e(e.getMessage(), e); } } return instance; } private DefaultSSLSocketFactory() throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { super(trustStore); TrustManager trustAllCerts = new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted( java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } @Override public void checkServerTrusted( java.security.cert.X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } }; //初始化509凭证信任管理器 sslContext.init(null, new TrustManager[]{trustAllCerts}, null); //设置所有请求都会得到客户端的信任 this.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { //连接SSL Socket return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } }
当然上面的通信谈不上SSL加密,因此使用了https和没使用https请求没啥区别,就像http一样。
对于https安全请求的的加密过程,我们需要充分的认识,简单的说他是一个加密的过程。
对于这个过程的请求才叫安全请求,那么这个请求是怎么构建的呢
一般来说证书放在assets或者raw资源文件下(以下代码来自互联网,用户可以再第一段代码中稍作修改,便可使用)
public void getHttpsKeyStore(){ AssetManager am = context.getAssets(); InputStream ins = am.open("robusoft.cer"); try { //读取证书 CertificateFactory cerFactory = CertificateFactory.getInstance("X.509"); //问1 Certificate cer = cerFactory.generateCertificate(ins); //创建一个证书库,并将证书导入证书库 KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC"); //问2 keyStore.load(null, null); keyStore.setCertificateEntry("trust", cer); return keyStore; } finally { ins.close(); } }
将这里代码整合到第一段代码中,形成https安全请求,当然也可以单独使用,
public static HttpClient getAndroidHttpClient(int connTimeout, String userAgent,int retryTimes) { //...... AssetManager am = context.getAssets(); InputStream ins = am.open("robusoft.cer"); try { //读取证书 CertificateFactory cerFactory = CertificateFactory.getInstance("X.509"); //问1 Certificate cer = cerFactory.generateCertificate(ins); //创建一个证书库,并将证书导入证书库 KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC"); //问2 keyStore.load(null, null); keyStore.setCertificateEntry("trust", cer); //把咱的证书库作为信任证书库 SSLSocketFactory socketFactory = new SSLSocketFactory(keystore); schemeRegistry.register(new Scheme("https", socketFactory , 443)); } finally { ins.close(); } // ...... }