×

《淘宝TOP API Sign签名算法详解与MD5/HMAC实现(Python/Java/PHP)》

万邦科技Lex 万邦科技Lex 发表于2026-06-23 09:08:34 浏览20 评论0

抢沙发发表评论

🔐 淘宝TOP API Sign签名算法详解与MD5/HMAC实现(Python/Java/PHP)

淘宝开放平台(TOP)所有请求必须带 sign参数,签名错误是TOP接入排名第一的坑。弄懂下面规则,所有接口(商品/订单/物流/退款)一遍通。

一、TOP签名算法官方规范(重点记这4步)

假设:
  • app_key = 123456

  • app_secret = 6a8b0c1d2e3f4g5h

  • API参数:method=taobao.item.get, num_iid=654321, fields=title,price, format=json, v=2.0

✅ Step 1:收集所有API参数

包括公共参数app_key, method, timestamp, format, v, sign_method)+业务参数num_iid, fields等)
剔除sign字段、值为空(None/空字符串) 的参数、二进制文件字段

✅ Step 2:按参数名 ASCII 升序排序

app_key=123456
fields=title,price
format=json
method=taobao.item.get
num_iid=654321
sign_method=md5
timestamp=1700000000000
v=2.0

✅ Step 3:拼接 key+value(无=无&)

app_key123456fields title,priceformatjsonmethodtaobao.item.getnum_iid654321sign_methodmd5timestamp1700000000000v2.0
⚠️ 注意 fields的值是 title,price,原样拼入,逗号不编码不省略

✅ Step 4:计算签名

■ MD5方式(最常用)
待签名串 = APP_SECRET + 上步字符串 + APP_SECRET
sign = MD5(待签名串).upper()
例:6a8b0c1d2e3f4g5h + 拼接串 + 6a8b0c1d2e3f4g5h→ MD5 → 32位大写
■ HMAC-MD5方式(可选,需传 sign_method=hmac-md5
sign = HMAC_MD5(AppSecret, 拼接串).hexdigest().upper()
拼接串首尾不再加AppSecret,直接用排序后KV串做HMAC原文。

二、Python实现(推荐直接用)

# top_sign.py
import hashlib
import hmac
from typing import Dict, Optional

# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
def top_sign(params: Dict[str, Optional[str]], app_secret: str,
             sign_method: str = "md5") -> str:
    """
    淘宝TOP API签名
    params: 所有API参数(不含sign, 空值已剔除)
    app_secret: AppSecret
    sign_method: 'md5' | 'hmac-md5'
    return: 大写签名串
    """
    # 1. 剔除空值 & sign
    filtered = {
        k: v for k, v in params.items()
        if v is not None and str(v).strip() != "" and k != "sign"
    }

    # 2. ASCII升序
    sorted_items = sorted(filtered.items(), key=lambda x: x[0])

    # 3. 拼 key+value
    qs = "".join(f"{k}{v}" for k, v in sorted_items)

    # 4. 签名
    if sign_method == "hmac-md5":
        sig = hmac.new(
            app_secret.encode("utf-8"),
            qs.encode("utf-8"),
            hashlib.md5
        ).hexdigest().upper()
    else:  # md5
        raw = f"{app_secret}{qs}{app_secret}"
        sig = hashlib.md5(raw.encode("utf-8")).hexdigest().upper()

    return sig


# ===================== 验证示例 =====================
if __name__ == "__main__":
    APP_SECRET = "YOUR_APP_SECRET"

    api_params = {
        "method": "taobao.item.get",
        "app_key": "YOUR_APP_KEY",
        "timestamp": str(1700000000000),   # 替换真实毫秒时间戳
        "format": "json",
        "v": "2.0",
        "sign_method": "md5",
        "num_iid": "654321098765",
        "fields": "title,price,pic_url"
        # "session": "ACCESS_TOKEN"  # 订单类接口需要
    }

    sign = top_sign(api_params, APP_SECRET, sign_method="md5")
    api_params["sign"] = sign

    print("✅ 计算签名:", sign)
    print("📦 请求参数示例:")
    for k, v in api_params.items():
        print(f"   {k} = {v}")

三、Java实现

// TopSignUtil.java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.*;
# 封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
public class TopSignUtil {

    public static String signMD5(Map<String, String> params, String appSecret) throws Exception {
        // 1. 去空 & 去sign
        Map<String, String> filtered = new TreeMap<>();
        for (Map.Entry<String, String> e : params.entrySet()) {
            if (!"sign".equals(e.getKey()) && e.getValue() != null && !e.getValue().trim().isEmpty()) {
                filtered.put(e.getKey(), e.getValue());
            }
        }

        // 2. TreeMap已按key升序,拼KV
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> e : filtered.entrySet()) {
            sb.append(e.getKey()).append(e.getValue());
        }

        // 3. 首尾加secret → MD5
        String toSign = appSecret + sb.toString() + appSecret;
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] digest = md.digest(toSign.getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder();
        for (byte b : digest) {
            hex.append(String.format("%02X", b & 0xFF));
        }
        return hex.toString();
    }

    public static String signHmacMD5(Map<String, String> params, String appSecret) throws Exception {
        Map<String, String> filtered = new TreeMap<>();
        for (Map.Entry<String, String> e : params.entrySet()) {
            if (!"sign".equals(e.getKey()) && e.getValue() != null && !e.getValue().trim().isEmpty())
                filtered.put(e.getKey(), e.getValue());
        }

        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> e : filtered.entrySet()) {
            sb.append(e.getKey()).append(e.getValue());
        }

        Mac mac = Mac.getInstance("HmacMD5");
        mac.init(new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacMD5"));
        byte[] raw = mac.doFinal(sb.toString().getBytes("UTF-8"));
        StringBuilder hex = new StringBuilder();
        for (byte b : raw) hex.append(String.format("%02X", b & 0xFF));
        return hex.toString();
    }

    // 用法:
    // Map<String,String> p = new HashMap<>();
    // p.put("method","taobao.item.get"); ...
    // String sign = TopSignUtil.signMD5(p, APP_SECRET);
}

四、PHP实现

<?php
// top_sign.php
function topSign(array $params, string $appSecret, string $method = 'md5'): string {
    // 1. 去空去sign
    unset($params['sign']);
    $filtered = array_filter($params, fn($v) => $v !== null && $v !== '');

    // 2. key升序
    ksort($filtered);

    // 3. 拼KV
    $str = '';
    foreach ($filtered as $k => $v) {
        $str .= $k . $v;
    }

    // 4. 签名
    if ($method === 'hmac-md5') {
        return strtoupper(hash_hmac('md5', $str, $appSecret));
    }
    return strtoupper(md5($appSecret . $str . $appSecret));
}

// 示例
$params = [
    'method'   => 'taobao.item.get',
    'app_key'  => 'YOUR_APP_KEY',
    'timestamp'=> (string)(microtime(true)*1000),
    'format'   => 'json',
    'v'        => '2.0',
    'sign_method'=>'md5',
    'num_iid'  => '654321098765',
    'fields'   => 'title,price,pic_url'
];
echo "sign = " . topSign($params, 'YOUR_APP_SECRET') . PHP_EOL;
?>

五、签名排错清单(TOP特有)

现象
原因
排查
Invalid Signature
时间戳单位错
必须 13位毫秒 int(time.time()*1000)
Invalid Signature
空值参入签名
严格过滤 None/''
Invalid Signature
fields值含空格被截断
保持原串,title,price逗号分隔不动
偶发成功偶失败
系统时钟偏差大
服务器NTP同步(±5分钟内)
HmacMD5返回签名错
sign_method未传或传 hmac-md5
API参数加 "sign_method":"hmac-md5"
图片上传接口签错
二进制不参与签名
仅业务参数字段签,Filedata放 multipart body

六、面试一句话

淘宝TOP签名 = 参数按key ASCII升序拼key+value→ 首尾加AppSecret → MD5 → 大写(Hmac-MD5变体不加首尾Secret,直接用HMAC);空值/sign不参入,timestamp必须用13位毫秒。
需要我把这个 top_sign()集成进之前的 商品详情 / 订单同步 / OAuth Client 给你完整可运行TOP对接模块吗?


群贤毕至

访客