很多开发者都踩过这个坑:微信小程序辛辛苦苦提交审核,结果被拒,理由要么是“内容安全检测未通过”,要么直接是“未接入内容安全检测”。其实说白了,就是两个核心接口——`msgSecCheck` 和 `imgSecCheck`——没调通,或者调了但根本没生效。可不是简单地加个SDK、发个请求就万事大吉了。参数填错、图片路径不对、token过期、文件太大、返回结果没判全,任何一个环节掉链子,审核都别想过。
### 文本检测千万别只看 `errcode`,`suggest` 才是关键
很多人检测文本时,只检查 `errcode` 是不是等于 0,等于 0 就认为通过了,这是最大的坑。你要明白,`errcode` 只能说明你的请求成功发到了微信
服务器,网络和凭证没问题,但内容本身到底安不安全,得看返回的 `suggest` 字段。
- `suggest: "pass"` → 内容安全,可以直接展示给用户。
- `suggest: "review"` → 内容可疑,需要进入人工审核队列。这时候既不能直接拒绝,也不能直接放行。
- `suggest: "risky"` → 明确违规,必须拦截,否则小程序会被强制下线。
需要警惕的是,虽然 `errcode === 87014` 通常对应 `suggest === "risky"`,但这不是绝对的。有些旧版本的接口返回,可能 `errcode === 0` 但 `suggest === "risky"`。所以,代码逻辑里必须读取 `suggest` 来做最终决策,这才是最保险的做法。
### `imgSecCheck` 接口对图片路径和大小非常挑剔
在 ThinkPHP 里调用 `$app->content_security->checkImage()` 时,传参的路径必须是服务端可以读取的**绝对本地路径**。这一点非常关键,很多开发者在这上面反复栽跟头。不能用网络 URL,不能用相对路径,更不能是 `public/` 目录下那种通过 HTTP 就能直接访问的地址。
正确的做法是:用户上传图片后,先用 `moveTo()` 把文件存到 `runtime/image/` 这类非公开目录下,然后把完整的文件路径(比如 `/www/project/runtime/image/abc.jpg`)传给 `checkImage()`。
此外,图片文件大小必须严格限制在 `1MB` 以内,超限的话接口会直接返回 `errcode: 41005`。建议在上传环节就用 `getimagesize()` 预先判断大小,超标的直接通过 `imagejpeg()` 压缩后再保存,避免运行时出错。
还有一点:这个接口不支持 GIF 动图鉴黄,传了会返回 `errcode: 87016`。所以,如果收到 GIF,需要提前转换成 JPEG 或 PNG 静态图再检测。另外,不要以为临时路径就能用,某些 Linux 发行版上,`/tmp/phpXXXXXX` 这类路径会因为 `open_bindir` 的限制而读取失败,直接报错。
### AccessToken 缓存失效,会导致批量检测全部失败
很多 ThinkPHP 项目图省事,每次检测都重新去拉取 `access_token`。但微信对 `access_token` 的调用有频率限制,2 小时内超过 2000 次就会触发限流。一旦被限流,后续所有的 `msgSecCheck` 和 `imgSecCheck` 请求都会返回 `errcode: 40001`,直接崩掉。
正确的做法是,必须用数据库或者 Redis 把 `access_token` 和它的 `expires_in`(过期时间)缓存起来。写入缓存时,要预留至少 300 秒的缓冲时间,比如 `expires_in` 是 7200 秒,那缓存时间就设为 6900 秒,这样能避免 token 刚过期就遇到并发刷新请求。
在 ThinkPHP5/6 中,推荐使用 `cache('wx_access_token', $token, 7000)`,这种方式比文件缓存更可靠,而且能规避文件锁的问题。如果你用了 EasyWeChat SDK,要特别注意检查它有没有开启 `debug` 模式下的强制刷新逻辑。有些低版本的 SDK,在 `dev` 环境下会忽略缓存,每次都要重新获取,上线前一定要确认清楚。上线后,定期查看日志,搜索 `"errcode":40001`,如果高频出现,立刻排查缓存是否写入失败或者缓存目录不可写。
### 敏感词过滤必须和微信 API 联动,不能只靠本地库
微信的 `msgSecCheck` 接口虽然强大,但覆盖不了所有的语义变体。比如“波霸”、“果冻”、“水蜜桃”这类代称式的敏感词,它可能就识别不出来。反过来,只靠本地敏感词库又容易误杀,像“苹果手机”这种正常词汇也会被误判。所以,两者必须配合使用,而且顺序不能错。
正确的流程是:先调用微信 API,拿到 `suggest`。如果结果是 `"pass"`,才进行下一步;如果是 `"review"` 或 `"risky"`,直接终止流程,根据结果处理。
然后,再跑本地的敏感词库。这一步只对 `suggest === "pass"` 的内容做二次扫描。如果命中了涉政、暴恐等高危词,就需要将结果降级为 `"review"`,交给人工复核。
本地词库的实现也有技巧,不要用 `strpos()` 暴力扫描,性能差还容易有边界问题。建议预编译成 AC 自动机,或者用 `preg_match()` 带上 `\b` 边界符,这样能更精准地匹配,避免“王八”匹配到“王八蛋”但漏掉“王八羔子”这种问题。还有,别把词库硬编码在 PHP 文件里。最好放在 `config/sensitive_words.php` 文件中,并设置为只读,方便运营人员随时热更新。
最后,分享一个最容易被忽略的点:微信内容安全接口的返回结果里,`trace_id` 字段是唯一的调试凭证。每次调用都要把它记录下来。当某条内容被误判为 `risky` 时,拿着这个 `trace_id` 去微信后台的“内容安全控制台”,就能查到当时检测的原始图片或文本快照。很多时候,光看代码逻辑看不出问题,只有看了微信那边到底接收到的是什么,才能找到症结所在。
本文转载于:https://www.php.cn/faq/2436907.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。