编程 微信公众号服务号服务器对接:验签、AES 消息加解密与 access_token 刷新踩坑

2026-09-08 00:04:22

微信公众号(服务号)服务器对接踩坑记录

接入前配置与首次验签

在公众号后台「基本配置」里填好服务器 URL、Token、EncodingAESKey,并勾选消息加解密方式(明文 / 兼容 / 安全模式)。提交配置时,微信会 GET 一次回调地址,带上 signaturetimestampnonceechostr 四个参数做校验:

  1. 将 Token、timestamp、nonce 三个字符串按字典序排序后拼接成一个字符串;
  2. 对该字符串做 SHA1;
  3. 结果与 signature 比对,一致则原样返回 echostr 完成接入。

注意:字典序比较按字符逐字节进行,不是按字符串长度比较。

消息与事件推送

用户发消息或触发事件后,微信 POST 到服务器:

  • 明文模式:请求体是 XML,直接读。
  • 安全模式(推荐):消息体需要解密后再处理,响应的消息也要加密。

安全模式加解密(AES-256-CBC)

EncodingAESKey 与密钥

EncodingAESKey 是 43 位 Base64 字符串,解密密钥为:

key = Base64_Decode(EncodingAESKey + "=")

IV(初始向量)取解密后密钥的前 16 字节。

解密

AES-256-CBC 解密后的明文包结构:

random(16字节随机串) + msg_len(4字节网络字节序) + 消息体XML + AppID

解密步骤:

  1. AES-256-CBC 解密;
  2. 去掉 PKCS7 padding(padding 每个字节的值等于补位长度);
  3. 取后 4 字节网络字节序得到 msg_len
  4. msg_len 截出消息体 XML;
  5. 校验尾部 AppID 是否与自己的 AppID 一致,防止伪造。

加密

加密为解密的反过程:

  1. 拼接 random + msg_len + xml + appid
  2. 按 PKCS7 补位到 32 的倍数;
  3. CBC 加密后 Base64 输出。

有两个容易被攻击的点:

  • padding 校验不要泄露具体错误信息,使用常量时间比较,防止 oracle padding 攻击;
  • AppID 校验失败直接拒绝,不要返回详细异常。

验签与调试建议

  • 签名比对用 hash_equals(PHP)或同类常量时间比较函数,不要用 ==
  • 本地联调别直接拿微信后台触发推送,先用官方「消息加解密库」的各语言 demo 把加解密跑通,再对接业务。

相关文档:

access_token 缓存与刷新

access_token 有效期 7200 秒,必须自己缓存(Redis / 内存均可),不能每次请求都现调接口获取。

官方建议提前 5 分钟左右刷新。多实例部署时注意缓存击穿:加分布式锁,只让一个实例去拉取新 token,其他实例等待并复用新 token。

过期时调用接口返回 errcode: 40001 invalid credential,此时应清理缓存并重试一次。

被动回复的超时与幂等

微信要求收到消息后 5 秒内回复,否则会重试 3 次。回复内容为明文或加密 XML,Content 节点用 CDATA 包裹。

业务处理耗时(查库、调第三方 API)时,正确姿势:

  1. 先把消息落队列 / 异步处理;
  2. 快速返回空串或转发到客服。

否则超时后微信重试会导致重复下单 / 重复发送——这是被动回复最容易踩的幂等坑。

推荐文章

程序员茄子在线接单