当volley和okhttp遇上https

今天看Volley和OkHttp的时候看见了https的相关东西,忽然想到上次老婆叫帮忙买票用google浏览器上12306的场景了。。。

为什么今天会去看这三个东西

最近有点清闲的感觉,有一定的时间去做自己想做的东西。于是就在网上搜索着网络库。出现大量关于Volley,okhttp的文章。其中volleyokhttp结合使用的文章太多了。首先我分别用了两个库来访问网络。
第一感觉是我懵逼了。原因是:都是访问网络的库,为什么要结合使用?实在也是没太明白。后面经过一段实践后终于有所明白。

1.volleypost方式

 public void getNetWorkString() {

        String strul = "http://www.baidu.com";
        StringRequest stringRequest = new StringRequest(Request.Method.POST, strul, new Response.Listener() {
            @Override
            public void onResponse(String s) {
                show.setText(s);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.i("joker", volleyError.getMessage().toString());
            }
        }
        ) {
            @Override
            protected Map getParams() throws AuthFailureError {
                Map map = new HashMap();
                map.put("joker", "1");
                map.put("chan", "2");
                return super.getParams();
            }
        };
        queue.add(stringRequest);
        queue.start();
 }

效果如下:

当volley和okhttp遇上https_第1张图片

2.okhttpget方式

public void getTest() {
        String url = "https://www.baidu.com/";
        final Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.i("joker", e.getMessage());
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                final String res = response.body().string();
                show.setText(res);
            }
        });
}

效果如下:

当volley和okhttp遇上https_第2张图片

相信这个时候已经知道是为什么了。因为okhttponResponseonFailure并不在主线程。所以不能直接更新ui

现在改写代码为

public void getTest() {
        String url = "https://www.baidu.com/";
        final Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.i("joker", e.getMessage());
            }

            @Override
            public void onResponse(final Response response) throws IOException {
                final String res = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        show.setText(res);
                    }
                });
            }
        });
}

就能正常的运行获取数据了。
这时候已经明白了一点为什么要用volley+okhttp的方式了。因为okhttp是最基本的网络库,需要封装一次才更实用。为volley已经封装好了一些。而volley的传输层实现又没okhttp好用,所有就有现在的用okhttp作为volley的传输层的实现方式了。(以上只是自己的理解,有不对的地方希望指出来),下面来看看具体的实现:
首先是OkHttp2.0已经不支持OkHttpStack了。所有现在用OkUrlFactory

public class OkHttpStack extends HurlStack {

    private final OkUrlFactory okUrlFactory;

    public OkHttpStack() {
        this(new OkUrlFactory(new OkHttpClient()));
    }

    public OkHttpStack(OkUrlFactory okUrlFactory) {
        if (okUrlFactory == null) {
            throw new NullPointerException("Client must not be null.");
        }
        this.okUrlFactory = okUrlFactory;
    }

    @Override
    protected HttpURLConnection createConnection(URL url) throws IOException {
        return okUrlFactory.open(url);
    }
}

其中用到OkUrlFactory需要导入对应jar

然后这样使用

requestQueue = Volley.newRequestQueue(getContext(), new OkHttpStack());
requestQueue.start(); 

到此okhttpvolley之前的迷惑我算是知道暂时明白了。

https的引出

百度百科是这样说的:

HTTPS(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。这个系统的最初研发由网景公司(Netscape)进行,并内置于其浏览器Netscape Navigator中,提供了身份验证与加密通讯方法。现在它被广泛用于万维网上安全敏感的通讯,例如交易支付方面。

很多网站都是支持https,像我们平时的百度可以用https:/www.baidu.com访问。一般情况下支持https的网站基本都是CA机构颁发的证书,默认情况下是可以信任的。但是有些网站是通过自签名的方式进行的,并不是CA机构去颁发的。使用自签名证书的网站,大家在使用浏览器访问的时候,一般都是报风险警告,其中12306购票网站就是这样做的,https://kyfw.12306.cn/otn/,点击进入12306的购票页面就能看到了。

当volley和okhttp遇上https_第3张图片

下面我们试试用volleyokhttp访问12306(https://www.12306.cn)。

1.volley

 public void getVNetWorkString() {

        String strul = "https://www.12306.cn";
        StringRequest stringRequest = new StringRequest(strul, new Response.Listener() {
            @Override
            public void onResponse(String s) {
                show.setText(s);
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                Log.i("joker", volleyError.getMessage().toString());
            }
        });
        queue.add(stringRequest);
        queue.start();
 }

2.okhttp

 public void getONetWorkString() {
        OkHttpClient okHttpClient = new OkHttpClient();
        String url = "https://www.12306.cn";
        final Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.i("joker", e.getMessage());
            }

            @Override
            public void onResponse(final com.squareup.okhttp.Response response) throws IOException {
                final String res = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        show.setText(res);
                    }
                });
            }
        });
}

运行程序会出现下面的信息:

03-29 14:01:42.856 12928-12928/com.jokerchan.volleytest I/joker: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

从中可以知道是证书不支持的原因。

网上常见的做法是信任所有证书,则是重写X509TrustManager为空实现,代码如下:

1.忽略证书的方法

public void ignoreCard(OkHttpClient client) {

        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init
                    (
                            null,
                            new TrustManager[]{new AllX509TrustManager()},
                            new SecureRandom()
                    );
            client.setSslSocketFactory(sslContext.getSocketFactory());
            client.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
}

2.访问https连接

 public void getONetWorkString() {
        String url = "https://www.12306.cn";
        final Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.i("joker", e.getMessage());
            }

            @Override
            public void onResponse(final com.squareup.okhttp.Response response) throws IOException {
                final String res = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        show.setText(res);
                    }
                });
            }
        });
 }

3.调用步骤

 ignoreCard(okHttpClient);
 getONetWorkString();

但是这样做很不安全。下面我们来看看如何用证书的方式进行验证。

1.首先到12306官方下载证书。进入首页就会看见。

当volley和okhttp遇上https_第4张图片

2.下载完成后解压得到srca.cer文件。

3.然后将他放在项目assets文件夹下面。这里可以随意放在任何地方,只要等读取到。

4.代码实现

证书设置

public void setCard(OkHttpClient client, InputStream certificate) {
    try {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        keyStore.load(null);
        String certificateAlias = Integer.toString(0);
        keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
        SSLContext sslContext = SSLContext.getInstance("TLS");
        final TrustManagerFactory trustManagerFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        sslContext.init
                (
                        null,
                        trustManagerFactory.getTrustManagers(),
                        new SecureRandom()
                );
        client.setSslSocketFactory(sslContext.getSocketFactory());
        client.setHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        });
    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (KeyManagementException e) {
        e.printStackTrace();
    }
}

访问

public void getONetWorkString() {
        String url = "https://www.12306.cn";
        final Request request = new Request.Builder().url(url).build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                Log.i("joker", e.getMessage());
            }

            @Override
            public void onResponse(final com.squareup.okhttp.Response response) throws IOException {
                final String res = response.body().string();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        show.setText(res);
                    }
                });
            }
        });
 }

调用

try {
    setCard(okHttpClient, getAssets().open("srca.cer"));
    } catch (IOException e) {
    e.printStackTrace();
 }
getONetWorkString();

上面两种方式都实现了访问https

我们也可以把证书里面的内容获取出来写成一个String

keytool -printcert -rfc -file srca.cer命令就可以获取到证书里面的内容。

当volley和okhttp遇上https_第5张图片

然后用String类型来存放。然后如此使用就可以了。

当volley和okhttp遇上https_第6张图片
setCard(okHttpClient, new Buffer().writeUtf8(cardInfo).inputStream());
getONetWorkString();

volley默认是不支持https的。既然有源码也可以自己尝试去改改。这里就不做过多解释了。

注意:其中
setHostnameVerifier
建议这样写
setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER),setHostnameVerifier
用来验证服务器证书上的域名是否和服务器的实际域名相匹配。(个人理解)

你可能感兴趣的:(当volley和okhttp遇上https)