本文定义 签名算法、安全校验、服务端配置项,并提供 JavaScript / Java / Python 的参考实现代码,直接可跑。
搭配《Webhook 接收消息 “响应体” 格式规范 · v1》一起使用。
Wxid
、MessageType
、Timestamp
、Signature
。{Wxid}:{MessageType}:{Timestamp}
(冒号分隔,原样字符串)secret
(共享密钥)expectSignature
expectSignature === body.Signature
(常量时间比较)bases = Wxid + ":" + MessageType + ":" + Timestamp
expect = HMAC_SHA256_HEX_LOWER(secret, bases)
assert expect == Signature
Timestamp
做时间偏移校验(如允许 ±15 分钟)。Data.messages[*].newMsgId
做去重(例如 LRU Set)。secret = "your-signature-secret"
:Wxid | MessageType | Timestamp | 计算明文 | 期望 Signature |
---|---|---|---|---|
wxid_xxxxxxxxxxxxxxxx | sync_message | 1757156304 | wxid_xxxxxxxxxxxxxxxx:sync_message:1757156304 | df5fdde88d2f3a9329cd0193969c0bac5a1d57e40cc6c28e181f478d3629c510 |
wxid_xxxxxxxxxxxxxxxx | sync_message | 1757156307 | wxid_xxxxxxxxxxxxxxxx:sync_message:1757156307 | 54cd72f857387f150ae84293ec35fb96e6347e9610ad2f1ee13025e245b72a80 |
两个向量均已核对无误,可用于联调自测。
{
"enabled": true, // 是否启用 webhook
"url": "http://0.0.0.0:8000/webhook", // 你的回调地址(供自检/文档)
"secret": "your-signature-secret", // 验签共享密钥
"includeSelfMessage": true, // 是否接收自己发出的消息
"messageTypes": ["*"], // ["*"] 或具体枚举,如 ["sync_message"]
"retryCount": 3, // 平台端或业务端重试策 略参考值
"timeout": 5, // 业务处理超时时间(秒)
"timestampSkewSec": 900, // 允许的时间偏移(秒),推荐 900 = 15 分钟
"dedupeMax": 5000 // 去重缓存容量(按 newMsgId/msgId 组合)
}
生产环境建议:密钥从安全管道(如 KMS / Vault / Secret Manager)注入,不要写死在代码里。
POST /webhook
(Content-Type: application/json
)GET /health
{ "ok": true, "message": "Webhook received" }
依赖: npm i express morgan
(Node.js ≥ 18)。
依赖: spring-boot-starter-web
(Maven 或 Gradle 均可)。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
依赖: pip install flask
(可选:pip install gunicorn
)。
rawContent
?msgType
的离线补 parser;线上只需先入库防丢。newMsgId|msgId
做幂等键,使用 LRU Set 或持久化 KV(如 Redis)去重。