天天看點

NoHttp之優雅的為參數簽名和資料加密NoHttp之優雅的為參數簽名和資料加密

NoHttp之優雅的為參數簽名和資料加密

NoHttp作為一個通俗易懂,簡單好用的網絡架構,用的人越來越多,是以大家的需求也就越來越豐富,本文主要介紹關于參數簽名和加密的優雅姿勢。

由于大家業務不同,故本文講述比較正常的token加密。

加密規則: 所有請求按照參數key按字典順序排序之後拼成A=a&B=b之後用MD5加密得出簽名值。

一,擷取請求參數

NoHttp擷取參數的方法是

Request#getParamKeyValues()

,NoHttp的作者-嚴振傑為我們封裝了一個MultiValueMap用于存儲一對多的

key-list<value>

,所有請求的參數最後都會添加到這個Map裡面。

在我翻閱了NoHttp的源碼後發現,細心的傑哥在NoHttp的

Request中

裡面提供了一個方法,

onPreExecute()

;

此方法在NoHttp的HttpConnection中被調用,翻出源碼一看,原來是真正執行請求前調用的,那麼我們得出這個方法是在子線程中調用,是以我們簽名加密之類的操作相對耗時,放在這裡再合适不過了:

/**
 * The connection is established, including the head and send the request body.
 *
 * @param request {@link IBasicRequest}.
 * @return {@link HttpURLConnection} Have been established and the server connection..
 * @throws Exception can happen when the connection is established and send data.
 */
private Network createNetwork(IBasicRequest request) throws Exception {
    //--------看這裡看這裡,在這裡調用了onPreExecute()。
    request.onPreExecute();

    // Print url, method.
    String url = request.url();
    Logger.i("Request address: " + url);
    Logger.i("Request method: " + request.getRequestMethod());

    Headers headers = request.headers();
    headers.set(Headers.HEAD_KEY_CONTENT_TYPE, request.getContentType());

    // Connection.
    List<String> values = headers.getValues(Headers.HEAD_KEY_CONNECTION);
    if (values == null || values.size() == )
        headers.add(Headers.HEAD_KEY_CONNECTION, Headers.HEAD_VALUE_CONNECTION_KEEP_ALIVE);

    // Content-Length.
    RequestMethod requestMethod = request.getRequestMethod();
    if (requestMethod.allowRequestBody())
        headers.set(Headers.HEAD_KEY_CONTENT_LENGTH, Long.toString(request.getContentLength()));

    // Cookie.
    headers.addCookie(new URI(url), NoHttp.getCookieManager());
    return mExecutor.execute(request);
}
           

是以我們仿照NoHttp中的預設請求繼承

RestRequest

類,重寫

onPreExecute()

方法,在這裡擷取所有參數并做簽名和加密:

public class DefineRequest extends RestRequest<String> {

    public DefineRequest(String url, RequestMethod method) {
        supper(url, method)
    }

    @Override
    public void onPreExecute() {
        MultiValueMap<String, Object> multiValueMap = getParamKeyValues();
        // TODO 解析來就是簽名以及加密了,且往下看。
    }

    @Override
    public String parseResponse(Headers header, byte[] body) throws Throwable {
        // 這裡把資料解析為String,直接用StringRequest的靜态方法。
        return StringRequest.parseResponseString(header, body);

        // 這裡如果你想自己解析:
        //1. 不指定編碼: return new String(body);
        //2. 指定編碼: return new String(body, "utf-8");
    }
}
           

二,請求參數加密

接下來的操作在代碼中一步一步解釋

/**
 * 參數加密
 */
@Override
public void onPreExecute() {
    //第一步:擷取所有請求參數
    MultiValueMap<String, Object> multiValueMap = getParamKeyValues();

    //第二步,定義List用于存儲所有請求參數的key
    List<String> keyList = new ArrayList<>();

    //第三步:定義Map用于存儲所有請求參數的value
    Map<String, String> keyValueMap = new HashMap<>();

    //第四步:拿到所有具體請求參數
    for (Map.Entry<String, List<Object>> paramsEntry : multiValueMap.entrySet()) {
        String key = paramsEntry.getKey();
        List<Object> values = paramsEntry.getValue();
        for (Object value : values) {
            if (value instanceof String) {

                //第五步:将請求參數的key添加到list中用于排序
                keyList.add(key);

                //第六步:将請求參數的value添加到Map中
                keyValueMap.put(key, (String) value);
            }
        }
    }

    //第七步:對請求參數key進行排序
    Collections.sort(keyList);

    StringBuilder paramsBuilder = new StringBuilder();

    //第八步:依次取出排序之後的key-value,并拼接
    for (String key : keyList) {
        String value = keyValueMap.get(key);
        paramsBuilder.append(key).append("=").append(value).append("&");
    }

    String params = "";
    if(paramsBuilder.length() > ) {
        //去掉最後一個&
        params = paramsBuilder.toString().substring(, paramsBuilder.length() - );
    }

    //第九步:對拼接好的參數進行MD5加密
    String token =  EncryptionUtil.md5(params);

    //最後,添加到請求參數
    add("token", token);

    // 如果你們伺服器要求添加到head,那麼:
    // addHeader("token", token);
}
           

擷取String的md5值的

EncryptionUtil

/**
 * 生成md5
 *
 * @param message
 * @return
 */
public static String md5(String message) {
    String md5str = "";
    try {
        //1 建立一個提供資訊摘要算法的對象,初始化為md5算法對象
        MessageDigest md = MessageDigest.getInstance("MD5");

        //2 将消息變成byte數組
        byte[] input = message.getBytes();

        //3 計算後獲得位元組數組,這就是那128位了
        byte[] buff = md.digest(input);

        //4 把數組每一位元組(一個位元組占八位)換成16進制連成md5字元串
        md5str = bytesToHex(buff);

    } catch (Exception e) {
        e.printStackTrace();
    }
    return md5str;
}


    /**
     * 二進制轉十六進制
     *
     * @param bytes
     * @return
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuffer md5str = new StringBuffer();
        //把數組每一位元組換成16進制連成md5字元串
        int digital;
        for (int i = ; i < bytes.length; i++) {
            digital = bytes[i];

            if (digital < ) {
                digital += ;
            }
            if (digital < ) {
                md5str.append("0");
            }
            md5str.append(Integer.toHexString(digital));
        }
        return md5str.toString().toLowerCase();
    }
           

好啦,到這裡就寫完啦,當然每個公司的簽名方法不盡相同,是以大概都是這個姿勢,這個姿勢是不是很優雅呢。