微信公众号php接口开发核心卡点是签名验证、消息加解密、access_token管理;签名需用token、timestamp、nonce字典序拼接后sha1加密比对;xml解析须禁用xxe并转义内容;access_token应存redis带过期自动管理。

微信公众号 PHP 接口开发不是“配个 token 就能用”,核心卡点在签名验证、消息加解密、access_token 管理这三块,漏一个就收不到消息或调不了接口。
怎么验证微信服务器发来的 signature?
微信每次推送事件或消息前,会带上 signature、timestamp、nonce 和 echostr(仅首次接入时),你必须用自己配置的 token 拼接后 SHA1 加密,结果和 signature 完全一致才算通过。
常见错误:直接用 $_GET['signature'] 和硬编码字符串比对;忽略参数排序(必须按字典序排 array($token, $timestamp, $nonce) 再 join);用 md5 或 base64 替代 sha1。
- PHP 必须用
sha1(implode('', $arr)),不能用hash('sha1', ...)(默认带二进制输出,要加true参数才等效) - 排序必须用
sort($arr, SORT_STRING),不能用asort(键名会被保留,导致拼接错乱) - 调试时把计算出的签名和微信给的
signature都var_dump出来,肉眼比对——微信不会告诉你哪里错了,只静默丢弃请求
收到 XML 消息后怎么安全解析并回复?
微信所有普通消息(文本、事件等)都走 POST 的 raw XML,file_get_contents('php://input') 是唯一可靠读法,$_POST 为空是正常现象。
立即学习“PHP免费学习笔记(深入)”;
容易踩的坑:用 simplexml_load_string 直接解析未过滤的 XML,可能触发 XXE 攻击;回复时没设 Content-Type 为 text/xml; charset=utf-8;时间戳用 time() 但没转成字符串(微信要求 CreateTime 是数字字符串)。
- 禁用外部实体:
libxml_disable_entity_loader(true)必须在simplexml_load_string前调用 - 回复 XML 中的
FromUserName和ToUserName必须互换,否则用户收不到 - 中文内容务必用
htmlspecialchars($content, ENT_QUOTES, 'UTF-8')转义,不然 XML 解析失败,微信当空响应
怎么安全持久化并复用 access_token?
access_token 有效期 2 小时,调用量有限(2000 次/天),不能每次请求都重新拉取。它必须存在本地(文件/Redis/DB),且要校验过期时间(微信返回的 expires_in 是秒数,不是绝对时间戳)。
典型翻车现场:多个 PHP 进程并发刷新,导致旧 token 被覆盖;用 file_put_contents 写文件没加锁,写坏 JSON;把 access_token 存 session 里(不同请求 session 不共享)。
- 推荐用 Redis:
$redis->setex('wx_access_token', 7200, $token),天然带过期,避免竞态 - 如果只能用文件,先
fopen(..., 'c')加锁,再flock($fp, LOCK_EX),写完flock($fp, LOCK_UN) - 每次使用前检查 Redis key 是否存在,不存在才去微信拉——别省那一次 HTTP 请求,反而更容易超限
最常被忽略的是消息加解密(尤其是企业微信或启用了消息加密的公众号),AES-CBC 模式下 encodingAesKey 必须补足 43 位 Base64 字符,少一位都会解密失败,而且错误不报具体原因,只返回空。别等上线才发现收不到消息才回头查这个。











