计算规则

为了防止恶意调用接口,调用任何一个接口都需要携带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&timestamp=20160907094600&voicecode=133435

2.URL(utf-8)编码后参数:
user=4006090002_dev&account=4006090002&callingid=010334555%2C18611338668&timestamp=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&timestamp=20160907094600&voicecode=133435&secret=F8B9E 0CC8A7428C7B2C57DBD06D1DC39

JAVA 示例代码

 String str = "appver=" + appver + "&timestamp=" + 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&timestamp=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