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();
}
好啦,到這裡就寫完啦,當然每個公司的簽名方法不盡相同,是以大概都是這個姿勢,這個姿勢是不是很優雅呢。