签名机制
业务 API 签名机制
概述
调用盟主云业务 API 时,服务端会对请求做身份校验。请求至少需要携带 appid 和 sign;建议同时携带 expired 控制请求有效期。签名不正确、appid 无效或已过期时,可能返回“签名校验失败”“auth failed”或参数错误。
本文适用于 api.zmengzhu.com 下的业务 API,例如:
https://api.zmengzhu.com/business/v1/user/createThirdUser
注意:/open/authorize/login 是开放授权登录跳转接口,签名和使用场景单独说明,详见开放授权登录文档。
公共参数
| 参数名 | 类型 | 必选 | 说明 |
|---|---|---|---|
appid |
string | 是 | 商户应用 ID,由盟主平台提供 |
expired |
int | 否 | 请求过期时间,10 位秒级 Unix 时间戳,建议设置为当前时间后 5 到 10 分钟 |
sign |
string | 是 | 请求签名,32 位 MD5 字符串 |
如果携带 expired,必须大于服务端当前时间。每次修改 expired 或业务参数后,都必须重新生成 sign。
请求格式
业务 API 通常使用以下格式:
POST https://api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999&sign=...
Content-Type: application/x-www-form-urlencoded
POST Body 使用表单参数,例如:
nickname=微信用户
third_uid=user-001
avatar=https://example.com/avatar.png
不建议使用 raw JSON 作为请求 body。当前业务 API 按表单参数读取 POST 数据,使用 JSON 可能导致服务端读不到参数,或导致签名字符串和实际请求不一致。
签名规则
第一步:准备 URL 参数
先准备除 sign 以外的 URL query 参数,例如:
appid=10000001&expired=1999999999
如果接口还有其他 GET 参数,也需要放在 query 中一起参与签名。最终请求时使用的 query 顺序,应和签名时保持一致。
第二步:生成 urlSuffix
urlSuffix 是去掉协议后的完整接口地址:
域名 + 路径 + ? + queryStringWithoutSign
示例:
api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999
注意:签名时不包含 http:// 或 https://。
第三步:准备 POST 表单参数
POST 参数不包含 sign,按参数名升序排序,然后将 key 和 value 直接拼接。
例如 POST 参数为:
{
"nickname": "微信用户",
"third_uid": "user-001",
"avatar": "https://example.com/avatar.png"
}
按 key 升序排序后为:
avatar
nickname
third_uid
拼接后的 sortString 为:
avatarhttps://example.com/avatar.pngnickname微信用户third_uiduser-001
注意:sortString 中没有 =,没有 &,POST 参数值参与签名时使用原始值,不使用 URL encode 后的值。
第四步:生成待签名字符串
urlSuffix + sortString + secret_key
示例:
api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999avatarhttps://example.com/avatar.pngnickname微信用户third_uiduser-001secret
第五步:计算 sign
对待签名字符串做 MD5:
md5(signSource)
得到:
ff3ed927e8c800ce843f38ba7d1d6f59
第六步:拼接最终请求 URL
https://api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999&sign=ff3ed927e8c800ce843f38ba7d1d6f59
完整示例
示例参数
接口:
POST https://api.zmengzhu.com/business/v1/user/createThirdUser
URL 参数:
{
"appid": "10000001",
"expired": 1999999999
}
POST 表单参数:
{
"nickname": "微信用户",
"third_uid": "user-001",
"avatar": "https://example.com/avatar.png"
}
示例密钥:
secret
每一步执行结果
queryStringWithoutSign:
appid=10000001&expired=1999999999
urlSuffix:
api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999
排序后的 POST 参数:
avatar
nickname
third_uid
sortString:
avatarhttps://example.com/avatar.pngnickname微信用户third_uiduser-001
signSource:
api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999avatarhttps://example.com/avatar.pngnickname微信用户third_uiduser-001secret
sign:
ff3ed927e8c800ce843f38ba7d1d6f59
最终请求 URL:
https://api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999&sign=ff3ed927e8c800ce843f38ba7d1d6f59
最终表单 Body:
nickname=%E5%BE%AE%E4%BF%A1%E7%94%A8%E6%88%B7&third_uid=user-001&avatar=https%3A%2F%2Fexample.com%2Favatar.png
PHP 示例
<?php
$appId = '10000001';
$secretKey = 'secret';
$host = 'api.zmengzhu.com';
$path = '/business/v1/user/createThirdUser';
$expired = 1999999999;
$postData = [
'nickname' => '微信用户',
'third_uid' => 'user-001',
'avatar' => 'https://example.com/avatar.png',
];
// URL 参数不包含 sign。
$queryStringWithoutSign = 'appid=' . $appId . '&expired=' . $expired;
$urlSuffix = $host . $path . '?' . $queryStringWithoutSign;
// POST 参数按 key 升序排序后拼接 key + value。
$signPostData = $postData;
ksort($signPostData);
$sortString = '';
foreach ($signPostData as $key => $value) {
$sortString .= $key . $value;
}
$signSource = $urlSuffix . $sortString . $secretKey;
$sign = md5($signSource);
$url = 'https://' . $urlSuffix . '&sign=' . $sign;
$formBody = http_build_query($postData);
echo "urlSuffix:\n" . $urlSuffix . "\n\n";
echo "sortString:\n" . $sortString . "\n\n";
echo "signSource:\n" . $signSource . "\n\n";
echo "sign:\n" . $sign . "\n\n";
echo "url:\n" . $url . "\n\n";
echo "formBody:\n" . $formBody . "\n";
输出结果:
urlSuffix:
api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999
sortString:
avatarhttps://example.com/avatar.pngnickname微信用户third_uiduser-001
signSource:
api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999avatarhttps://example.com/avatar.pngnickname微信用户third_uiduser-001secret
sign:
ff3ed927e8c800ce843f38ba7d1d6f59
url:
https://api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999&sign=ff3ed927e8c800ce843f38ba7d1d6f59
formBody:
nickname=%E5%BE%AE%E4%BF%A1%E7%94%A8%E6%88%B7&third_uid=user-001&avatar=https%3A%2F%2Fexample.com%2Favatar.png
curl 示例
curl -i -X POST 'https://api.zmengzhu.com/business/v1/user/createThirdUser?appid=10000001&expired=1999999999&sign=ff3ed927e8c800ce843f38ba7d1d6f59' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'nickname=微信用户' \
--data-urlencode 'third_uid=user-001' \
--data-urlencode 'avatar=https://example.com/avatar.png'
createThirdUser 参数说明
创建免授权用户接口:
POST https://api.zmengzhu.com/business/v1/user/createThirdUser
URL 公共参数:
| 参数名 | 类型 | 必选 | 说明 |
|---|---|---|---|
appid |
string | 是 | 商户应用 ID |
expired |
int | 否 | 请求过期时间,10 位秒级 Unix 时间戳,建议传入 |
sign |
string | 是 | 请求签名 |
POST 表单参数:
| 参数名 | 类型 | 必选 | 说明 |
|---|---|---|---|
third_uid |
string | 是 | 第三方平台用户唯一标识 |
nickname |
string | 是 | 第三方用户昵称,最长 16 个字符 |
avatar |
string | 是 | 第三方用户头像 URL |
成功返回示例:
{
"code": 200,
"msg": "ok",
"data": {
"uid": 12345678,
"atom": "xxxxxx"
}
}
注意:昵称字段名是 nickname,不是 name。
常见问题
sign 是否参与签名
不参与。先用不含 sign 的 URL 和 POST 参数生成签名,再把 sign 追加到最终请求 URL。
签名时是否包含协议
不包含。签名从域名开始:
api.zmengzhu.com/business/v1/user/createThirdUser?...
最终发起 HTTP 请求时才加上 https://。
POST 参数是否需要 URL encode 后参与签名
不需要。POST 参数参与签名时使用原始值,例如:
avatarhttps://example.com/avatar.png
真正发送请求时,表单 body 可以按 application/x-www-form-urlencoded 规则编码。
POST 参数顺序是否重要
发送请求时顺序不重要,但签名时必须按参数 key 升序排序后拼接。
可以使用 JSON body 吗
不建议。请使用:
Content-Type: application/x-www-form-urlencoded
expired 如何生成
expired 是 10 位秒级时间戳,建议设置为:
expired = 当前秒级时间戳 + 600
如果携带 expired,它必须大于服务端当前时间。expired 改变后,sign 必须重新生成。
返回 auth failed 怎么排查
auth failed 通常表示签名校验未通过或 appid 未通过认证。建议按以下顺序排查:
-
appid和secret_key是否属于同一个商户应用。 -
expired是否已经过期。 - 签名使用的 URL query 是否和最终请求 URL 完全一致。
- 是否把
sign也放进了待签名字符串。 - POST 参数是否按 key 升序排序后参与签名。
- 请求 Body 是否使用
application/x-www-form-urlencoded。 -
nickname字段是否误写成name。
