參考:基於java的https雙向認證,android上亦可用java
Android Https相關徹底解析 當OkHttp遇到Https
android
在android中,常常不可避免的的使用https和http訪問網絡數據,所以須要先構建一個網絡訪問clientapache
其中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是請求失敗後重試的規則app
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來支持全部主機經過socket
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同樣。ide
對於https安全請求的的加密過程,咱們須要充分的認識,簡單的說他是一個加密的過程。this
對於這個過程的請求才叫安全請求,那麼這個請求是怎麼構建的呢加密
通常來講證書放在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(); } // ...... }