mtop 签名:阿里系的"祖传配方"
淘宝 H5 的 mtop 接口签名是阿里系应用最广的一套方案,公式一句话说完:
sign = md5(token + "&" + t + "&" + appKey + "&" + data)token:cookie_m_h5_tk的下划线前段(_m_h5_tk=abc123_def456,取abc123)t:毫秒时间戳appKey:H5 端固定12574478(不同端不同值,App 端是另一套)data:业务参数的紧凑 JSON 字符串
公式简单到令人发指,但这套体系的精髓不在算法,在 token 的生命周期管理。
token 的"过期-刷新"循环
_m_h5_tk 不是登录才有的,匿名访问也会下发。它的获取方式很鸡贼:你直接请求接口,它故意返回令牌过期,同时在响应头里种新 token:
响应: {"ret":["FAIL_SYS_TOKEN_EXOIRED::令牌过期"]}
响应头: Set-Cookie: _m_h5_tk=abc123_def456; ...所以完整的客户端必须实现这个循环:
import hashlib, json, time, requests
class MtopClient:
def __init__(self):
self.session = requests.Session()
self.app_key = '12574478'
def _token(self):
tk = self.session.cookies.get('_m_h5_tk', domain='.taobao.com')
return tk.split('_')[0] if tk else ''
def request(self, api, data, v='1.0', retry=True):
t = str(int(time.time() * 1000))
data_str = json.dumps(data, separators=(',', ':'))
sign = hashlib.md5(f"{self._token()}&{t}&{self.app_key}&{data_str}".encode()).hexdigest()
resp = self.session.get(
f'https://h5api.m.taobao.com/h5/{api}/{v}/',
params={'jsv': '2.7.4', 'appKey': self.app_key, 't': t, 'sign': sign,
'api': api, 'v': v, 'type': 'originaljson', 'dataType': 'json',
'data': data_str}
)
ret = resp.json()
if any('TOKEN_EXOIRED' in r or '令牌过期' in r for r in ret.get('ret', [])):
if retry:
return self.request(api, data, v, retry=False) # cookie 已自动种下新 token
return ret注意 retry=False 防死循环——刷新后再失败就是别的问题了。
精髓:六个实测坑点
- data 序列化是头号杀手。必须
separators=(',', ':')紧凑无空格,key 顺序保持接口定义原样(Python dict 保序,按抓包顺序构造即可)。多空格、换行、key 乱序,签名全废。调试时把浏览器抓的 data 原文和你的生成结果做字符串 diff。 - appKey 分端不通用。H5 是
12574478,但同一个接口在 App 端是另一个 appKey,token 体系也不同。跨端抄参数必挂。 _m_h5_tk有配套兄弟_m_h5_tk_enc。部分敏感接口两个都校验,清 cookie 时要一起处理。- 域名体系。接口域名是
h5api.m.taobao.com,但 cookie 种在.taobao.com域。天猫业务走h5api.m.tmall.com,token 不通用。 - 签名对了只是入场券。高频请求会触发
FAIL_SYS_ACCESS_DENIED或要求滑块验证(x5sec 体系),那是行为风控,签名层无解,只能控速 + 代理池。 type参数影响返回格式。originaljson返回纯 json,jsonp返回回调包裹——协议模拟用前者,别给自己找解析麻烦。
一个完整的调试方法论
签名不对时按这个顺序排查:
- 抓浏览器真实请求,把
data参数原文复制出来,和你的data_str逐字符对比 - 确认 token 是下划线前段,没把
_def456部分算进去 - 确认 t 和 sign 用的是同一个 t(有人犯过签名用 t1、参数传 t2 的低级错误)
- 以上都对还失败,换一个全新会话重走"过期-刷新"流程,排除 token 污染
总结
- 公式
md5(token&t&appKey&data)十分钟实现,核心是 token 的过期-刷新循环 - data 紧凑 JSON、key 保序,是 90% 签名失败的原因
- appKey、token、域名三者成套,跨端不通用
- x5sec 滑块是另一套体系,签名解决不了,靠控速和代理