网络通信层搭建+https

https://blog.csdn.net/dodod2012/article/details/79202085

一、网络通信框架(Volley+https)

此次要讲的网络框架的封装是基于volley框架进行二次封装。底层采用httpUrlConnection进行网络访问,统一使用post方法请求网络。

1访问网络直接调用方法:ESBHttpUtils.getInstance().httpPost(String url, Map<String, ?> params, SystemProcessListener listener);其中ESBHttpUtils是我们封装的网络访问工具类,使用了单例设计模式(饿汉式)对ESBHttpUtils类进行了封装使得线程安全。

2.ESBHttpUtils类实现了一个VolleyRequestCancelable接口,并且实现了接口里面的方法cancelRequest,在cancelRequest方法里面通过使用Volley框架提供的cancelAll方法对请求进行取消操作,因此,我们在使用网络访问时可以使用该方法对网络请求进行取消。(在BaseActivity中在ondestroy方法中使用该方法使网络请求与activity生命周期联动)

3.getRequestQueue方法

ESBHttpUtils私有的构造方法中,对mContext进行了初始化,同时调用了getRequestQueue()方法对requestQueue初始化,初始化requestQueue的时候传入的是一个HurlStack对象,这也就意味着我们的网络框架最底层都是使用HttpURLConnection进行网络访问的。同时在HurlStack的构造方法中重写了createConnection方法,在此方法中进行了添加数字证书操作、设置请求头信息(User-agent

 

4.httpPostProcess实现方法

1)首先通过NetWorkUtil判断手机是否联网,没有联网的提示用户。

2)通过Volley框架提供的JsonObjectRequest方法发送网络请求,在该类的实例化中,实现了getHeaders()方法设置头信息,在getHeaders()方法中通过createHeaderParams()方法创建一个map,把一些公共属性比如appiddeviceIdappversion等放入了map集合中,然后把请求的参数requestParams(一个jsonObject)通过加签处理转换为一个String放入到map中,最后把该map作为一个请求头。

5.JsonRequest请求对 Response.Listener接口中onResponse方法的处理

onResponse实现方法中会去解析json中的Result字段,然后通过接口回调,把result值以及json传递给httpPostProcessString url, Map<String, ?> params, SystemProcessListener listener, SystemErrorListener errorListener)方法中listeler接口中:

 listener.divisionSystemProcess(code, response);

上代码:

public class ESBHttpUtils implements VolleyRequestCancelable {
    public static final int RESP_CODE_SUCCESS = 0;// 成功
    public static final int RESP_CODE_FAILED = -1;// 失败
    public static final int RESP_CODE_ERROR = -2000;// 错误

    public static final int ERROR_TYPE_UNKNOWN = -1000;
    public static final int ERROR_TYPE_ESBKEY_INVALID = 1001;
    public static final int ERROR_TYPE_TIMESTAMP_INVALID = 1002;
    public static final int ERROR_TYPE_TOKEN_INVALID = 1003;

    private RequestQueue mRequestQueue; // 请求队列
    private Context mContext; // 上下文
    private AppManager appManager = AppManager.getInstance();
    private int timeoutMs = 60;
    //判断是否跳转到登录页面
    private boolean toLogin = false;
    private String content_type = "application/json; charset=utf-8";
    private long timeDifference = 0;
    private int postCount = 0;

    /**
     * 获得一个http操作类
     *
     * @return
     */
    public static ESBHttpUtils getInstance() {
        return InstanceHolder.INSTANCE;
    }

    private static class InstanceHolder {
        private static final ESBHttpUtils INSTANCE = new ESBHttpUtils();
    }

    private ESBHttpUtils() {
        mContext = PPDaiApplication.getInstance().getApplicationContext();
        mRequestQueue = getRequestQueue();
    }

    /**
     * 得到一个 RequestQueue
     *
     * @return RequestQueue
     */
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            final String userAgent = Config.HTTP_USER_AGENT_PREFIX + AppUtil.getVersionName(PPDaiApplication.getInstance());
//            HttpClientStack stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            HurlStack stack = new HurlStack() {
                @Override
                protected HttpURLConnection createConnection(URL url) throws IOException {
                    HttpsUtils.allowAllSSL(mContext);
                    HttpURLConnection connection = super.createConnection(url);
                    connection.setInstanceFollowRedirects(false);
                    connection.setRequestProperty("User-agent", userAgent);
                    return connection;
                }
            };
            mRequestQueue = Volley.newRequestQueue(mContext, stack);
        }
        return mRequestQueue;
    }


    /**
     * handers添加数据
     *
     * @return
     */
    private Map<String, String> createHeaderParams() {

        Map<String, String> params = new HashMap<String, String>();

        params.put("APPID", Config.ESB_APP_ID);
        params.put("TIMESTAMP", dateFormat() + "");
        params.put("DEVICEID", AppUtil.getIMEI(mContext) + "");
        params.put("KEY", Config.ESB_SECRET_KEY_ID);
        params.put("KEYVERSION", ESBKeyUtils.getInstance().getKeyVersion() + "");
        params.put("APPVERSION", AppUtil.getVersionName(mContext) + "");
        String token = PreferencesUtils.getString(mContext, "token").trim();
        if (!TextUtils.isEmpty(token)) {
            params.put("TOKEN", token);
        }
        params.put("APPOS", "2");
        params.put("PRODUCTLINE", "1");
        params.put("PRODUCTID", "2");
        params.put("AID", Config.SERVER_APP_ID);
        return params;
    }

    private long dateFormat() {
        return (System.currentTimeMillis() / 1000) - timeDifference;
    }

    /**
     * @param url      请求的地址路径
     * @param params   请求的参数
     * @param listener 请求后状态的监听接口
     */
    public void httpPost(String url, Map<String, ?> params, SystemProcessListener listener) {
        httpPostProcess(url, params, listener, null);
    }

    /**
     * @param url           请求的地址路径
     * @param params        请求的参数
     * @param listener      请求后状态的监听接口
     * @param errorListener 请求失败的监听接口
     */
    public void httpPost(String url, Map<String, ?> params, SystemProcessListener listener, SystemErrorListener errorListener) {
        httpPostProcess(url, params, listener, errorListener);
    }

    /**
     * @param url           请求的地址路径
     * @param params        请求的参数
     * @param listener      请求后状态的监听接口
     * @param errorListener 请求后异常状态的监听接口
     */
    protected void httpPostProcess(final String url, final Map<String, ?> params, final SystemProcessListener listener, final SystemErrorListener errorListener) {
        if (!NetWorkUtil.checkNet(mContext)) {
            appManager.showTaost("当前网络不可用,请检查网络!");
            if (errorListener != null) {
                errorListener.error(null);
            } else {
                LoadManager.getInstance().dismiss();
            }
            return;
        }

        JSONObject jsonParams;
        if (params != null) {
            jsonParams = new JSONObject(params);
        } else {
            jsonParams = new JSONObject();
        }

        Log.e("volley", url + "  -------params >> " + jsonParams.toString());

        // 统一用post请求
        JsonObjectRequest postRequest = new JsonObjectRequest(url, jsonParams, new Response.Listener<JSONObject>() {

            @Override
            public void onResponse(JSONObject result) {
                postCount = 0;
                String response = result.toString();
                Logger.e("volley", url + "  -------onResponse >> " + response);
//                appManager.setLog(url + "  -------onResponse", response);
                try {
                    int code = result.getInt("Result");
                    if (listener != null) {
                        listener.divisionSystemProcess(code, response);
                    }
                } catch (JSONException e) {
//                    appManager.showTaost("JSONException" + e.getMessage());
                }
            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                LoadManager.getInstance().dismiss();
                if (error.networkResponse != null) {
                    if (error.networkResponse.headers == null) {
                        return;
                    }
                    String errorCode = error.networkResponse.headers.get("X-PPD-CODE");
                    int errorType = getErrorType(errorCode);
                    switch (errorType) {
                        case ERROR_TYPE_ESBKEY_INVALID: //重新请求esb key
                            ESBKeyUtils.getInstance().cleanKeys();
                            if (postCount < 5) {
                                postCount++;
                                ESBKeyUtils.getInstance().getClientCryptoKey(null);
                            }
                            break;
                        case ERROR_TYPE_TIMESTAMP_INVALID:// 时间戳无效
                            ESBKeyUtils.getInstance().cleanKeys();
                            saveTimeDifference(error.networkResponse.headers.get("X-PPD-TIMESTAMP"));
                            break;
                        case ERROR_TYPE_TOKEN_INVALID:// token无效,需要重新登录
                            appManager.showTaost("登录信息已过期,请重新登录");
                            ESBKeyUtils.getInstance().cleanKeys();
                            appManager.exitLogin();
                            if (!toLogin) {
                                appManager.isLogin(mContext);
                                toLogin = true;
                            }
                            return;
                    }
                }
                // error
                if (errorListener != null) {
                    errorListener.error(error);
                } else {
                    appManager.setLog("url-error", url);
                    appManager.showTaost("网络通信异常,请稍后再试");
                }
                appManager.setLog(url + "  -------onErrorResponse", error.getMessage() + "");
                if (error != null && error.networkResponse != null) {
                    appManager.setLog(url + "  -------onErrorResponse", JSON.toJSONString(error.networkResponse.headers));
                }
            }
        }) {
            /*
             * 设置头信息
             */
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> headers = getRequestHeaders(params);
//                Logger.e("volley", url + "  -------header >> " + JSON.toJSONString(headers));
//                appManager.setLog(url + "  -------header", JSON.toJSONString(headers));
                return headers;
            }

            @Override
            public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
                return super.setRetryPolicy(new DefaultRetryPolicy((int) TimeUnit.SECONDS.toMillis(timeoutMs), 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
            }

            @Override
            public String getBodyContentType() {
                return content_type;
            }

            @Override
            protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
                if (response.headers != null) {
                    saveTimeDifference(response.headers.get("X-PPD-TIMESTAMP"));
                }
                return super.parseNetworkResponse(response);
            }
        };

        // 生成 tag,  Activity/Fragment 生命周期结束时调用
        String tag = VolleyHttpLifeCircleHelper.getTagFromListeners(url, listener, errorListener);
        postRequest.setTag(tag);

//        appManager.setLog(url + "  -------params", JSON.toJSONString(params));

        System.setProperty("http.keepAlive", "false");
        mRequestQueue.add(postRequest);
    }

    @NonNull
    public Map<String, String> getRequestHeaders(Map<String, ?> params) {
        Map<String, String> headers = createHeaderParams();
        String signString = ESBKeyUtils.getInstance().getSignString(params);
        headers.put("X-PPD-SIGN", signString);
        headers.put("X-PPD-SIGNVERSION", "1");
        return headers;
    }

    private int getErrorType(String errorCode) {
        if ("SRV-BRQ-INVALIDSIGN".equals(errorCode) ||
                "SRV-BRQ-NOSIGN".equals(errorCode) ||
                "SRV-BRQ-NOKEY".equals(errorCode) ||
                "SRV-BRQ-INVALIDKEY".equals(errorCode) ||
                "SRV-BRQ-NOKEYVERSION".equals(errorCode) ||
                "SRV-BRQ-INVALIDKEYVERSION".equals(errorCode) ||
                "SRV-BRQ-OLDKEYVERSION".equals(errorCode) ||
                "GTW-BRQ-NOHEADER".equals(errorCode)) {
            return ERROR_TYPE_ESBKEY_INVALID;
        } else if ("GTW-BRQ-INVALIDTIMESTAMP".equals(errorCode)) {
            return ERROR_TYPE_TIMESTAMP_INVALID;
        } else if ("GTW-BRQ-INVALIDTOKEN".equals(errorCode)) {
            return ERROR_TYPE_TOKEN_INVALID;
        }
        return ERROR_TYPE_UNKNOWN;
    }

    /**
     * 保存本地时间和服务器的时间差
     *
     * @param timestamp
     */
    private void saveTimeDifference(String timestamp) {
        long serviceTimestamp = 0;
        if (!TextUtils.isEmpty(timestamp)) {
            serviceTimestamp = Long.parseLong(timestamp);
        }
        //保存本地和服务器时间差
        timeDifference = System.currentTimeMillis() / 1000 - serviceTimestamp;
    }

    @Override
    public void cancelRequest(final String tag) {
        if (mRequestQueue != null && tag != null) {
            mRequestQueue.cancelAll(new VolleyRequestFilter() {
                @NonNull
                @Override
                public Object getTag() {
                    return tag;
                }
            });
        }
    }

    public void httpGet(String url, final Response.Listener<String> listener, final Response.ErrorListener errorListener) {
        if (!NetWorkUtil.checkNet(mContext)) {
            if (errorListener != null) {
                errorListener.onErrorResponse(null);
            }
            return;
        }

        StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
            @Override
            public void onResponse(String s) {
                if (listener != null) {
                    listener.onResponse(s);
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {
                if (errorListener != null) {
                    errorListener.onErrorResponse(volleyError);
                }
            }
        }) {
            @Override
            public Request<?> setRetryPolicy(RetryPolicy retryPolicy) {
                return super.setRetryPolicy(new DefaultRetryPolicy((int) TimeUnit.SECONDS.toMillis(timeoutMs), 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
            }
        };

        String tag = VolleyHttpLifeCircleHelper.getTagFromListeners(url, listener, errorListener);
        request.setTag(tag);

        System.setProperty("http.keepAlive", "false");
        mRequestQueue.add(request);
    }

}

二、框架中https实现

public class HttpsUtils {

    private static TrustManager[] trustManagers;

//将认证机构的ca证书添加到系统默认的信任证书列表里面,供https通信时识别认证机构颁发的证书是否篡改使用
private static TrustManager[] getTrustManagers(Context context) throws Exception {
        // Load CAs from an InputStream
        // (could be from a resource or ByteArrayInputStream or ...)
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        // From https://www.washington.edu/itconnect/security/ca/load-der.crt
        InputStream caInput = new BufferedInputStream(context.getResources().openRawResource(R.raw.certification));
        Certificate ca;
        try {
            ca = cf.generateCertificate(caInput);
            System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
        } finally {
            caInput.close();
        }

        // Create a KeyStore containing our trusted CAs
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        return tmf.getTrustManagers();
    }

    public static void allowAllSSL(Context context) {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                // TODO Auto-generated method stub
                return true;
            }

        });

        try {
            if (trustManagers == null) {
                trustManagers = getTrustManagers(context);
            }
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustManagers, new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1)什么是https

HTTPSHTTP通信接口部分用SLL(Secure Socket Layer)TLS (Transport Layer Security) 协议替代而已.通常, HTTP直接和TCP通信,当使用SSL,演变成了先和SSL通信,再由SSLTCP通信了,简而言之,所谓HTTPS,其实就是身披SSL协议的这层外壳的HTTP.在采用SSL, HTTP就拥有了HTTPS的加密,证书和完整性的保护这些功能.SSL是独立于HTTP的协议,所有不光是HTTP协议,其他运行在应用层的SMTP(邮件协议)Telnet等协议均可配合SSL协议使用.可以说SSL是当今世界上应用最广泛的网络安全技术.

2)为什么要使用https通信

所有信息都是加密传输,第三方无法窃听。具有校验机制,一旦被篡改,通信双方都会立刻发现。配备身份证书,防止身份被冒充。http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。

3)https通信过程

通信步骤:

 

1)客户端请求服务器,发送握手消息

2)服务器返回客户端自己的加密算法、数字证书和公钥;

3)客户端验证服务器端发送来的数字证书是否与本地受信任的证书相关信息一致;如果不一致则客户端浏览器提示证书不安全如下图所示

如果验证通过,则浏览器会采用自身的随机数算法产生一个随机数,并用服务器发送来的公钥加密;发送给服务器;这里如果有人通过攻击获取了这个消息,那也没用,因为他没有解密此段消息所需要私钥;验证通过的网站在浏览器地址栏的右边会有一安全锁的标识;

3)服务器解密得到此随机数,并用此随机数作为密钥采用对称加密算法加密一段握手消息发送给浏览器;

4)浏览器收到消息后解密成功,则握手结束,后续的信息都通过此随机密钥加密传输。

以上是服务端认证的情况,如果服务端对访问的客户端也有认证需求,则客户端也需要将自己的证书发送给服务器,服务器认证不通过,通讯结束;原理同上;

什么是数字证书:

数字证书为发布公钥提供了一种简便的途径,其数字证书则成为加密算法以及公钥的载体,依靠数字证书,我们可以构建一个简单的加密网络应用平台,数字证书就好比我们生活中的身份证,现实中,身份证由公安机关签发,而网络用户的身份凭证由数字证书颁发认证机构CA签发,只有经过CA签发的证书在网络中才具备可认证性,CA并不是一个单纯的防御手段,它集合了多种密码学算法。.CER证书签发流程:


证书的签发过程实际上是对申请数字证书的公钥做数字签名,证书的验证过程实际上是对数字证书的公钥做验证签名,其中还包含证书有效期验证,通过CA数字证书,我们对网络上传输的数据进行加密/解密和签名/验证操作,确保数据机密性、完整性、抗否认性、认证性,保证交易实体身份的真实性,保证网络安全性。

 

 

图解https:

 

如有不懂欢迎咨询,本人qq:1342923917

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章