7.8 KiB
第三方人机验证优化改造计划
约束:不写双路/兼容迁移代码;顺手清理死代码;不做真实视觉效果验收(用语法检查、契约测试、构建产物校验代替)。 已确认决策:范围全做(P0+P1+P2);下载场景 pass 放行 10 分钟;凭据不齐时仅加后台警告、前台保持放行。
改动文件总览
| 文件 | 动作 |
|---|---|
inc/modules/captcha.php |
重写校验层 + 瘦身渲染 + 后台警告 + 静态缓存 |
assets/js/captcha-front.js |
新建(从内联脚本迁移,~340 行) |
assets/css/shared/captcha.css |
保留不动(改为进 bundle) |
tools/build-public-assets.mjs |
publicScripts 加 captcha-front.js;public.css 加 captcha.css |
src/Admin/Schema/SchemaPermissions.php |
删除 'mode' 死字段(L32-34) |
assets/admin/src/schema/sections/permissions/index.js |
删除 'mode' 死字段(L18 defaults、L50-56) |
src/Admin/SchemaProvider.php |
L403 白名单删 'mode';保存后 flush 配置静态缓存 |
tests/widget-system/captcha-cloud-contract.test.cjs |
新建契约测试 |
不动的文件及原因:aksrt-header-userbox.php(toast-contract 测试锁定 signin 接线)、data-aksrt-download-captcha 挂载点(templates/parts/resource-download-box.php:467、inc/helpers/aksrt-pay.php:1586)、所有业务 JS(auth/signin/comments/cms-download/user-center/front-submit 只消费 AKSRTCaptchaState.scenes[scene] 与 mount 内 token input,接口不变即零改动)。
1. inc/modules/captcha.php — 校验层
P0-1 腾讯云改走 WP HTTP API:aksrt_verify_tencent() 删除 curl 块(L374-390,含 CURLOPT_SSL_VERIFYPEER => false),改用 wp_remote_post('https://captcha.tencentcloudapi.com/', ['timeout'=>5, 'headers'=>aksrt_tencent_tc3_sign(...)生成的一维数组, 'body'=>$payload]),与另外三家一致。签名函数不动。
P1-5 错误信息收敛:四家服务商的传输层错误(wp_remote_post 返回 WP_Error、响应不可解析)统一 error_log('[AKSRT Captcha] {provider}: ' . 详情) 后返回通用文案 __('人机验证服务暂不可用,请稍后重试', 'aksrt');保留对用户有行动意义的映射文案(腾讯 CaptchaCode 表、阿里 F0xx 表)。腾讯 API Error 分支(签名错误等)也走 log+通用文案。
P1-4:阿里云 SignatureNonce 由 uniqid('', true) 改 wp_generate_uuid4()。
P1-8:四家 'timeout' 统一 5 秒(现 10 秒)。
P1-6 Turnstile 绑定场景:aksrt_verify_captcha($post_data, $scene = '') 增加第二参数(唯一调用方是 aksrt_require_captcha,无散落调用点);Cloudflare 成功响应校验:action !== 'aksrt-verify' 或 cdata !== $scene(存在才比对,lenient)→ 失败。客户端配套见 §2。
P0-2 下载 pass 放行(10 分钟):aksrt_require_captcha() 内,scene 命中放行名单时先查 transient、验证通过后写 transient:
aksrt_captcha_pass_ttl($scene):仅download返回10 * MINUTE_IN_SECONDS,其余返回 0(单一代码路径,无散落分支)- key =
'aksrt_captcha_pass_' . md5($scene . '|' . get_current_user_id() . '|' . aksrt_get_client_ip() . '|' . md5(UA)),仿aksrt_auth_rate_既有模式
2. assets/js/captcha-front.js — 新建(P2-9 + P1-7 客户端)
把 aksrt_render_captcha() 内联 <script>(L632-969)整体迁移为静态文件,逻辑不改名不重写:loadScriptOnce、四家 load*、AKSRTCaptchaState.scenes[scene].verify/reset 注册、setField/resetVerificationState/syncDownloadSiblings 等原样搬移。仅两处行为修正:
- 极验
onClose空回调改为cancelPending('已取消验证')(P1-7,与腾讯/CF 取消体验对齐) - Turnstile render 配置加
cdata: C.scene(P1-6 客户端配套)
激活模型:扫描 document.querySelectorAll('[data-aksrt-captcha-config]'),JSON.parse(el.getAttribute(...)) 逐个 init;DOMContentLoaded 后再补扫一次并暴露 window.AKSRTCaptchaState.rescan() 供未来 ajax 注入使用。保持 vanilla JS、无新依赖。
对应 PHP 渲染端瘦身:aksrt_render_captcha() 只输出
- 原 wrapper 结构不变(
.aksrt-captcha-wrap+#uidbox +#vidcaptcha_verified 隐藏域), - wrapper 上挂
data-aksrt-captcha-config(esc_attr(wp_json_encode($js_config))), - 删除内联
<script>与逐次<link rel="stylesheet">。
TOKEN_FIELDS 常量、i18n 字符串原样进 JS 文件(与仓库其它 bundle 内硬编码中文一致)。
3. 构建与后台(P2-10 + bundle 接线)
tools/build-public-assets.mjs:publicScripts追加'assets/js/captcha-front.js';cssBundles['public.css']追加'assets/css/shared/captcha.css'。走既有 bundle 版本机制,无需新增 enqueue 代码。SchemaPermissions.php:删除 'mode' 字段定义(L32-34,单选项 always 的死字段)。assets/admin/src/schema/sections/permissions/index.js:删除mode的 defaults(L18)与字段定义(L50-56)。SchemaProvider.phpL403:归一化白名单去掉'mode';write_section_settings()更新aksrt_captcha_config后调用新助手aksrt_captcha_config_reset()清静态缓存(function_exists 守卫,与现有 captcha 专用块并排)。- DB 里残留的
mode键不做迁移清洗(读路径白名单已忽略它,符合"不做迁移"约束)。
4. inc/modules/captcha.php — 后台警告与缓存
- P0-3:新增顶层
add_action('admin_notices', ...):manage_options权限 +enabled为真 +active_provider非空 +aksrt_captcha_provider_is_configured()为假时,输出notice-error横幅,文案指向「功能&权限 → 人机验证」补全配置(复用现有 misconfig 提示文案)。前台行为不变(保持放行)。 - P2-11:
aksrt_get_captcha_config()加static $cache;新增aksrt_captcha_config_reset();写入路径(SchemaProvider)调用 reset。aksrt_get_captcha_status_summary()的 12 场景循环随之受益。
5. 新契约测试 tests/widget-system/captcha-cloud-contract.test.cjs
仿 toast-contract 的正则断言风格:
captcha.php:不匹配curl_init|CURLOPT_SSL_VERIFYPEER|machine-verification|slidercaptcha|imagecaptcha;匹配wp_remote_post(≥4 处)、wp_generate_uuid4、'timeout'\s*=>\s*5、aksrt_captcha_pass_;渲染函数不匹配/<script|<link/(内联抽取完成)captcha-front.js存在且含AKSRTCaptchaState、data-aksrt-captcha-configbuild-public-assets.mjs的 publicScripts 含 captcha-front.js、public.css 含 shared/captcha.cssSchemaPermissions.php与 adminindex.js的 captcha 段不匹配modeSchemaProvider.php不匹配'mode'
6. 验证(不做视觉验收)
php -l全部改动的 PHP 文件node --checkcaptcha-front.jsnode tools/build-public-assets.mjs重建 bundlegrep全仓清扫:无curl_init、无'mode'(captcha 相关文件)、无 legacy 标记node --test tests/widget-system/*.test.cjs全量跑,预期仅存量 page-display-contract 一项失败(与本次无关,已验证在 HEAD 上同样失败)
明确不做
- 不做视觉验收/浏览器实测(按要求)
- 不写新旧配置迁移、不清洗 DB 残留
mode键 - 不动腾讯 trerror 容灾票据策略(维持拒绝)
- 不做阿里云签名 V3 升级、Turnstile appearance/retry 配置项(列为后续可选项)
- 不动
ExtensionController的 extensions 状态端点(SPA 未消费属另一话题)
建议提交拆分(如需提交)
fix(captcha): use WP HTTP API with TLS verification for tencentfeat(captcha): short-lived download pass + turnstile binding + error hardeningrefactor(captcha): extract inline runtime to captcha-front.js bundlechore(captcha): drop dead mode field, config cache, contract tests