API 签名生成规则文档
文档目录
一、文档概述
本文档统一规范秒信通平台所有接口请求签名(signature)生成逻辑,所有业务接口请求必须携带合法签名,服务端通过相同规则校验签名合法性,防止参数篡改、非法请求;同时补充CURL请求兼容配置说明及多语言完整签名工具代码。
二、签名整体流程
- 整理请求参数,剔除
signature字段 - 参数按键升序排序
- 对键、值执行URL编码+指定字符替换
- 拼接基础参数字符串(QueryString)
- 拼接请求方式、路径、基础参数,生成待加密原始串
- 使用产品密钥通过HmacSHA1加密原始串
- 加密结果Base64编码,最终得到
signature签名
三、分步详细生成规则
步骤1:构造基础QueryString
1.1 参数筛选
取出接口全部业务请求参数,移除signature签名字段,剩余所有参与签名计算。
1.2 参数排序
对剩余参数的key(参数名)按照ASCII字典升序排序。
1.3 键值编码与字符替换
分别对排序后每一组key、value执行标准URLEncode,编码完成后执行3项字符替换:
| 原字符 | 替换目标字符 |
|---|---|
| + | %20 |
| * | %2A |
| %7E | ~ |
1.4 拼接基础QueryString
单组参数格式:编码后key=编码后value
多参数使用&分隔拼接,示例:
name=%E5%BC%A0%E4%B8%89&age=25
1.5 拼接请求方式+接口路径,生成待加密完整串
拼接格式:
HTTP请求方法 + "&" + URLEncode("/") + "&" + 上一步基础QueryString
- HTTP请求方法大写:GET/POST/PUT/DELETE等;
- 固定拼接
&%2F&(/URL编码后为%2F);
示例(POST请求,参数name=张三、age=25):
POST&%2F&name=%E5%BC%A0%E4%B8%89&age=25
步骤2:计算最终签名signature
- 加密算法:HmacSHA1
- 加密密钥:平台分配的产品密码(SecretKey)
操作顺序:
- 将步骤1生成的完整待加密字符串作为加密原文;
- 使用产品密钥执行HmacSHA1哈希,得到二进制摘要;
- 对二进制摘要做Base64编码,编码结果即为接口请求参数
signature。
四、请求头兼容特殊说明
重要提示:平台全部接口不支持CURL 100-Continue协议,若请求接口响应超时、卡顿,需关闭该协议
PHP CURL示例配置
$ch = curl_init($apiUrl);
// 关闭100-Continue协议头
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Expect:"));
// 其他curl配置...
其他语言(Java/Python/Go)同理,请求头移除/清空Expect字段即可。
五、调用示例(伪代码逻辑)
输入条件
- 请求方式:POST
- 产品密钥:
123456Secret - 请求参数:
name=张三&sex=男&signature=xxx
1. 剔除signature,剩余参数
name=张三、sex=男
2. key升序排序
name、sex
3. URL编码+字符替换拼接基础QueryString
name=%E5%BC%A0%E4%B8%89&sex=%E7%94%B7
4. 拼接待加密原始串
POST&%2F&name=%E5%BC%A0%E4%B8%89&sex=%E7%94%B7
5. HmacSHA1+Base64
使用密钥123456Secret加密上述字符串,Base64输出结果作为signature放入请求参数。
六、各语言完整签名工具代码
所有代码内置字符替换、排序、HmacSHA1、Base64逻辑,复制即可直接调用测试
6.1 PHP 完整签名工具
sign.php
<?php
/**
* 生成接口签名 signature
* @param string $method 请求方式大写 GET/POST
* @param array $params 全部请求参数数组
* @param string $secret 产品密钥
* @return string
*/
function buildSign(string $method, array $params, string $secret): string
{
// 1. 移除签名字段
unset($params['signature']);
// 2. 按键升序排序
ksort($params);
// 3. URL编码 + 字符替换
$kvList = [];
foreach ($params as $k => $v) {
$encodeK = urlencode($k);
$encodeV = urlencode((string)$v);
// 规则替换
$encodeK = str_replace(['+', '*', '%7E'], ['%20', '%2A', '~'], $encodeK);
$encodeV = str_replace(['+', '*', '%7E'], ['%20', '%2A', '~'], $encodeV);
$kvList[] = "{$encodeK}={$encodeV}";
}
$queryStr = implode('&', $kvList);
// 4. 拼接待加密原文
$rawStr = $method . "&" . urlencode("/") . "&" . $queryStr;
// 5. HmacSHA1 + base64
$hmac = hash_hmac('sha1', $rawStr, $secret, true);
return base64_encode($hmac);
}
// ========== 测试示例 ==========
$secretKey = "123456Secret";
$reqMethod = "POST";
$reqParams = [
'name' => '张三',
'sex' => '男',
'signature' => ''
];
$sign = buildSign($reqMethod, $reqParams, $secretKey);
echo "生成签名:" . $sign;
?>
6.2 Java 完整签名工具(JDK8+)
SignUtil.java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class SignUtil {
private static final String ALGORITHM = "HmacSHA1";
/**
* 生成签名
* @param method 请求大写 GET/POST
* @param params 请求参数Map
* @param secret 产品密钥
* @return signature
* @throws Exception
*/
public static String buildSign(String method, Map<String, String> params, String secret) throws Exception {
// 1. 移除signature
params.remove("signature");
// 2. key升序排序
TreeMap<String, String> sortedMap = new TreeMap<>(params);
List<String> kvArr = new ArrayList<>();
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
String k = entry.getKey();
String v = entry.getValue();
// url编码
String ek = URLEncoder.encode(k, StandardCharsets.UTF_8.name());
String ev = URLEncoder.encode(v, StandardCharsets.UTF_8.name());
// 字符替换
ek = ek.replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
ev = ev.replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
kvArr.add(ek + "=" + ev);
}
String queryStr = String.join("&", kvArr);
// 拼接原始串
String raw = method + "&" + URLEncoder.encode("/", StandardCharsets.UTF_8.name()) + "&" + queryStr;
// HmacSHA1
SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM);
Mac mac = Mac.getInstance(ALGORITHM);
mac.init(keySpec);
byte[] hmacByte = mac.doFinal(raw.getBytes(StandardCharsets.UTF_8));
// Base64
return Base64.getEncoder().encodeToString(hmacByte);
}
// 测试入口
public static void main(String[] args) throws Exception {
String secret = "123456Secret";
String method = "POST";
Map<String, String> param = new HashMap<>();
param.put("name", "张三");
param.put("sex", "男");
param.put("signature", "");
String sign = buildSign(method, param, secret);
System.out.println("签名结果:" + sign);
}
}
6.3 Python3 完整签名工具
sign.py
import hmac
import hashlib
import base64
from urllib.parse import quote
def build_sign(method: str, params: dict, secret: str) -> str:
"""
生成接口签名
:param method: 请求大写 GET/POST
:param params: 请求参数字典
:param secret: 产品密钥
:return: signature
"""
# 1. 删除签名字段
params.pop("signature", None)
# 2. 按键升序
sorted_items = sorted(params.items(), key=lambda x: x[0])
kv_list = []
for k, v in sorted_items:
# url quote 等价urlencode
ek = quote(str(k), safe='')
ev = quote(str(v), safe='')
# 字符替换规则
ek = ek.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
ev = ev.replace("+", "%20").replace("*", "%2A").replace("%7E", "~")
kv_list.append(f"{ek}={ev}")
query_str = "&".join(kv_list)
# 拼接待加密字符串
slash_encode = quote("/", safe='')
raw_str = f"{method}&{slash_encode}&{query_str}"
# HmacSHA1
hmac_obj = hmac.new(secret.encode("utf-8"), raw_str.encode("utf-8"), hashlib.sha1)
digest = hmac_obj.digest()
# base64编码返回
return base64.b64encode(digest).decode("utf-8")
# 测试示例
if __name__ == "__main__":
secret_key = "123456Secret"
req_method = "POST"
req_params = {
"name": "张三",
"sex": "男",
"signature": ""
}
sign_result = build_sign(req_method, req_params, secret_key)
print("生成签名:", sign_result)
七、校验注意事项
- 参数大小写敏感:参数key区分大小写,排序严格区分ASCII码;
- 空值参数:参数值为空字符串也必须参与签名计算,不可丢弃;
- 编码标准:使用RFC3986标准URL编码,必须执行规定的3种字符替换;
- 密钥安全:产品密钥仅服务端与业务方后端持有,禁止前端明文泄露;
- 签名时效性:建议接口搭配timestamp时间戳防重放(可拓展接入参数);
- 多语言统一:PHP/Java/Python编码字符集统一UTF-8,否则签名不一致。
八、常见错误排查
1. 签名校验失败
- 未移除signature参数参与加密;
- 参数未按key升序;
- URL编码后未执行+/*/~字符替换;
- 请求方法大小写错误(小写post不匹配);
- HmacSHA1密钥使用错误、字符编码非UTF-8、Base64格式异常。
2. 接口请求超时
CURL未清空Expect请求头,触发100-Continue等待。