参考:基于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();
}
// ......
}