签名机制

概述

API接口服务会对每个访问的请求进行身份验证,所以无论使用 HTTP 还是 HTTPS 协议提交请求,都需要在请求中包含签名(sign)信息。

签名步骤

API服务接口通过使用appid和secret进行对称加密的方法来验证请求的发送者身份,每个接口必须填写以下公共参数:

参数名 类型 必选 参数说明
appid string 开通API服务时获取的平台唯一id
expired int 签名失效的unix时间戳,建议当前时间5分钟后
sign string 请求接口的参数生成的签名,详细生成规则见 (签名机制)

签名规则

请将请求URL 拼接上 排序拼接后的POST的数据 拼接上 秘钥字符串,然后对拼接出来的字符串计算MD5值。

伪代码示例

// 接口地址
url = https://api.zmengzhu.com/message/delete

// 密钥id与secret
appid = 20191008135xxxxxxxx
secret = Nd9zTE1eli1PlKy4ZdSsKAWpiNNsOOEaAfUzOxVcGvDC47q5QYX1pJtfJZLPkr0q

// 接口请求有效期,当前时间戳加5分钟
expired = nowTimeStamp + 300

// 拼接url参数 注意如有GET请求参数,可以依次添加,参数顺序不做要求
url = url + '?appid=' + appid + '&expired=' + expired;

// POST参数非必须,GET请求方式时,postDataMap为空
postDataMap = [
  'ticket_id' => 2
  'msg_id' => 1,
];

// 如有POST数据,需要对postDataMap内容进行排序,以索引名正序排序
ksort(postDataMap);

// 排序后的结果
/*
postDataMap = [
    'msg_id' => 1
    'ticket_id' => 2,
];
*/

// 如有POST数据,需要拼接POST参数,拼接POST数据的key和value
postString = '';
for (key in postDataMap) 
     postString = postString + key + postDataMap[key]
// 拼接后postString的值为 msg_id1ticket_id2

// 开始签名 注意postString可以为空, urlSuffix 为去除请求协议后的地址,
// 如 url = 'http://www.zmengzhu.com' 则 urlSuffix = 'www.zmengzhu.com'
urlSuffix = preg_replace(/^https?:\/\//, '', url)

sign = md5(urlSuffix + postString + secret)

// 最终完整请求URL
url = url + '&sign=' + sign;

代码示例 (Java版本)

package test;


import java.security.MessageDigest;
import java.util.*;


public class SignDemo {

    public static void main(String[] args) {
        SignDemo demo=new SignDemo();
//      以下为post请求签名方式
//        Map<String, String> postParam = new LinkedHashMap<>();
//        postParam.put("offset","0");
//        postParam.put("limit","10");
//        String sign=demo.md5Sign(demo.noHttpUrl("http://api.t.zmengzhu.com/business/v1/channel/lists?appid=2019091711385214738"),
//		demo.sortparams(postParam),"BKyE0nD7q0dRPHw5PxGL83LLnx8Pcu90GRBFnhfGwFnlmE7iO6wFI7FZsYu6Ir6e");
//        System.out.println("http://api.zmengzhu.com/business/v1/xxx/xxxxx?appid=xxxx"+"&"+"sign="+sign);//完整的请求地址
//        以下为get请求签名方式
        String sign=demo.md5Sign(demo.noHttpUrl("http://api.zmengzhu.com/business/v1/xxx/xxxxx?appid=xxxx&参数key=值&参数key=值"),
		null,"密钥");
        System.out.println("http://api.zmengzhu.com/business/v1/xxx/xxxxx?appid=xxxx&参数key=值&参数key=值"+"&"+"sign="+sign);//完整的请求地址
    }

	
    public Map<String,Object> sortparams(Map<String,String> params){
        Map<String,Object> result=new LinkedHashMap<>();
        List<Map.Entry<String,String>> list = new ArrayList<Map.Entry<String,String>>(params.entrySet());
        Collections.sort(list,new Comparator<Map.Entry<String,String>>() {
            //升序排序
            public int compare(Map.Entry<String, String> o1,
                               Map.Entry<String, String> o2) {
                return o1.getKey().compareTo(o2.getKey());
            }

        });

        for(Map.Entry<String,String> mapping:list){
            result.put(mapping.getKey(),mapping.getValue());
            System.out.println(mapping.getKey()+":"+mapping.getValue());
        }
        return result;
    }

    /**
     * 去掉请求头
     * @param urlHttp
     * @return
     */
    private String noHttpUrl(String urlHttp){
        if(urlHttp.contains("http://")){
            return urlHttp.replace("http://","");
        }else if(urlHttp.contains("https://")){
            return urlHttp.replace("https://","");
        }
        return urlHttp;
    }

    /**
     * 获取sign
     * @param url 去除前缀的绝对路径
     * @param postParam post 参数传入
     * @param secretKey
     * @return
     */
    public static String md5Sign(String url, Map<String, Object> postParam,String secretKey) {
        StringBuilder sb=null;
        String sign="";
        if (postParam != null && postParam.size() > 0) {
            sb = new StringBuilder();
            if (postParam != null && postParam.size() > 0) {
                List<String> paramNames = new ArrayList<String>(postParam.size());
                paramNames.addAll(postParam.keySet());
                for (String paramName : paramNames) {
                    String value = String.valueOf(postParam.get(paramName));
                    sb.append(paramName).append(value);
                }
            }
        }
        if (sb != null && !isEmpty(sb)) {
            System.out.println(url + sb.toString() + secretKey);
            sign= getMd5(url + sb.toString() + secretKey);
        }else {
            System.out.println(url + secretKey);
            sign=getMd5(url + secretKey);
        }

        return sign;
    }

    public static boolean isEmpty(StringBuilder string){
        if(string==null||string.equals("")||string.length()==0){
            return true;
        }else {
            return false;
        }
    }

    public static String getMd5(String value) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(value.getBytes());
            return toHexString(md5.digest());
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "";
    }

    private static final char HEX_DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
	'a', 'b', 'c', 'd', 'e', 'f' };

    public static String toHexString(byte[] b) {
        StringBuilder sb = new StringBuilder(b.length * 2);
        for (int i = 0; i < b.length; i++) {
            sb.append(HEX_DIGITS[(b[i] & 0xf0) >>> 4]);
            sb.append(HEX_DIGITS[b[i] & 0x0f]);
        }
        return sb.toString();
    }
}

代码示例 (PHP版本)

$appid = "xxx"; // 开通平台获取的appid
$secret = "xxx"; // 开通平台获取的secret

$mzServiceUrl = 'http://api.zmengzhu.com'; // 盟主接口服务域名
$action = '/live/create'; // 请求服务操作

$params = [
	"appid" => $appid,
	"expired" => time() + 300,
	// 此处可以添加其他业务接口单独需要的参数
];

$urlSuffix = str_replace(['http://', 'https://'], '', $mzServiceUrl) . $action . '?' . http_build_query($params);

$postData = [
	// 需要post请求的参数
];

$sortString = '';
if ($postData) {
	ksort($postData);
	foreach ($postData as $k => $v) {
		$sortString .= $k.$v;
	}
}

return md5($urlSuffix  . $sortString . $secret);