计算规则
为了防止恶意调用接口,调用任何一个接口都需要携带secret,服务端会根据请求参数,对secret进行验证,secret不合法的请求将会被拒绝。
secret 计算过程如下:
1. 所有的请求和响应数据编码皆为 utf-8 格式,URL 里的所有参数名和参数值做 URL 编码.
2. 对所有 API 请求参数(包括所有参数,但除去 secret 参数和 byte[]类型的参数),根据参数名称的 ASCII 码表的顺序排序。如:foo=1, bar=2, foo_bar=3, foobar=4,排序后的顺序是 bar=2, foo=1, foo_bar=3, foobar=4。
3. 将排序好的参数名和参数值拼装在一起,根据上面的示例得到的结果为:bar=2foo=1foo_bar=3foobar=4。
4. 把拼装好的字符串采用 utf-8 编码,在拼装的字符串后加上 token(通过登录系统授权获取,登录 api 时可使用 pwd作为该参数),再进行 Md5 的加密,格式如:md5(bar2foo1foo_bar3foobar4token)。
5. 将 加 密 得 到 的 字 节 流 结 果 使 用 十 六 进 制 表 示 , 如 :hex("helloworld".getBytes("utf-8")) = “68656C6C6F776F726C64”,说明:MD5 是 128 位长度的加密算法,用 16 进制表示,一个十六进制的字符能表示 4 个位,所以 secret 的字符串长度固定为 32 个十六进制字符。
secret 计算示例:
1. token: a66e422b-20b5-49e2-92ff-49db46ae9cfa
post所需参数:
user=4006090002_dev&account=4006090002&callingid=010334555,18611338668×tamp=20160907094600&voicecode=133435
2.URL(utf-8)编码后参数:
user=4006090002_dev&account=4006090002&callingid=010334555%2C18611338668×tamp=20160907094600&voicecode=133435
**注:逗号转换**
3. key 重新排序后的参数:
account4006090002callingid010334555%2C18611338668timestamp20160907094600user4006090002_devvoicecode133435
**注:此处请将=、&等符号去除**
4. secret 计算:
MD5(account4006090002callingid010334555%2C18611338668timestamp20160907094600user4006090002_devvoicecode133435a66e422b-20b5-49e2-92ff-49db46ae9cfa)=F8B9E0CC8A7428C7B2C57DBD06D1DC39
5. 发送的 post 参数: user=4006090002_dev&account=4006090002&callingid=010334555%2C18611338668×tamp=20160907094600&voicecode=133435&secret=F8B9E 0CC8A7428C7B2C57DBD06D1DC39
JAVA 示例代码
String str = "appver=" + appver + "×tamp=" + timestamp + "&user=" + user + "&secret=" + secret;
Map<String, String[]> map = queryStrParse2(str);
String secret =signTopRequest(map, token);
public static Map<String, String[]> queryStrParse2(String queryStr) {
Map<String, String[]> mapRequest = new LinkedHashMap<String, String[]>();
String[] arrSplit = null;
if (queryStr == null) {
return mapRequest;
}
arrSplit = queryStr.split("[&]");
for (String strSplit : arrSplit) {
String[] arrSplitEqual = new String[2];
arrSplitEqual = strSplit.split("[=]");
// 解析出键值
if (arrSplitEqual.length > 1) {
// 正确解析
mapRequest.put(arrSplitEqual[0], new String[]{arrSplitEqual[1]});
} else {
if (arrSplitEqual[0] != "") {
// 只有参数没有值,不加入
mapRequest.put(arrSplitEqual[0], new String[]{new String("")});
}
}
}
return mapRequest;
}
public static String signTopRequest(Map<String, String[]> params, String token) {
// 删除参数secret和值,不参与计算
Map<String, String[]> map = new HashMap<String, String[]>();
map.putAll(params);
map.remove("secret");
// 第一步:检查参数是否已经排序
String[] keys = map.keySet().toArray(new String[0]);
Arrays.sort(keys);
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder();
for (String key : keys) {
String[] value = map.get(key);
if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value[0])) {
query.append(key).append(value[0]);
}
}
// 第三步:使用MD5加密
query.append(token);
// 第四步:把二进制转化为大写的十六进制
return getMD5(query.toString(), 32);
}
public static String getMD5(String sourceStr, int bt) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
result = buf.toString();
if (bt == 16) {
return buf.toString().substring(8, 24);
}
if (bt == 32) {
return result;
}
} catch (NoSuchAlgorithmException e) {
System.out.println(e);
}
return result;
}
Http 示例: 请求:
POST /api/call/queryVoiceCode.action HTTP/1.1 Accept: text/html, */*
Content-Type: application/x-www-form-urlencoded Host: localhost:8080
Content-Length: 278
user=4006090002_dev&account=4006090002&callingid=010334555%2C186 11338668×tamp=20160907094600&voicecode=133435&secret=F8B9E 0CC8A7428C7B2C57DBD06D1DC39
响应:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1 Content-Length: 56
{"data":[],"errmsg":"未查询到该数据","errcode":0}
文档更新时间: 2023-09-07 11:37 作者:admin