From 88eb29c645a9b01fbad125cacefd329c04411542 Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 29 Aug 2026 17:03:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E9=97=AE=E9=A2=98fix(captcha)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ss_6a5711b4-7c56-4453-a8c1-805094668edc.md | 95 ++++ .../src/schema/sections/permissions/index.js | 11 - assets/css/build/public.css | 24 + assets/css/build/public.min.css | 2 +- assets/js/build/public.js | 409 ++++++++++++++ assets/js/captcha-front.js | 406 ++++++++++++++ inc/modules/captcha.php | 524 +++++------------- src/Admin/Schema/SchemaPermissions.php | 3 - src/Admin/SchemaProvider.php | 5 +- tools/build-public-assets.mjs | 2 + 10 files changed, 1074 insertions(+), 407 deletions(-) create mode 100644 .zcode/plans/plan-sess_6a5711b4-7c56-4453-a8c1-805094668edc.md create mode 100644 assets/js/captcha-front.js diff --git a/.zcode/plans/plan-sess_6a5711b4-7c56-4453-a8c1-805094668edc.md b/.zcode/plans/plan-sess_6a5711b4-7c56-4453-a8c1-805094668edc.md new file mode 100644 index 00000000..94693f53 --- /dev/null +++ b/.zcode/plans/plan-sess_6a5711b4-7c56-4453-a8c1-805094668edc.md @@ -0,0 +1,95 @@ +# 第三方人机验证优化改造计划 + +约束:不写双路/兼容迁移代码;顺手清理死代码;不做真实视觉效果验收(用语法检查、契约测试、构建产物校验代替)。 +已确认决策:范围全做(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()` 内联 ` 'Cloudflare Turnstile', 'value' => 'cloudflare'], ['label' => '极验 GeeTest', 'value' => 'geetest'], ]], - ['key' => 'mode', 'label' => '触发模式', 'type' => 'radio', 'default' => 'always', 'dependsOn' => ['key' => 'enabled', 'value' => true], 'options' => [ - ['label' => '始终显示', 'value' => 'always'], - ]], ['key' => 'enabled_scenarios', 'label' => '启用场景', 'type' => 'checkbox', 'default' => ['comment', 'login', 'register', 'bind', 'bind_phone', 'lostpassword', 'change_password', 'change_email', 'download', 'signin'], 'dependsOn' => ['key' => 'enabled', 'value' => true], 'options' => [ ['label' => '发表评论', 'value' => 'comment'], ['label' => '用户登录', 'value' => 'login'], diff --git a/src/Admin/SchemaProvider.php b/src/Admin/SchemaProvider.php index b7c3b7cb..3cc8bf42 100644 --- a/src/Admin/SchemaProvider.php +++ b/src/Admin/SchemaProvider.php @@ -400,7 +400,7 @@ class SchemaProvider { // Captcha: always surface the normalized positive scene list. if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_get_captcha_config')) { $normalized = aksrt_get_captcha_config(); - foreach (['enabled', 'active_provider', 'enabled_scenarios', 'tencent', 'aliyun', 'cloudflare', 'geetest', 'mode'] as $key) { + foreach (['enabled', 'active_provider', 'enabled_scenarios', 'tencent', 'aliyun', 'cloudflare', 'geetest'] as $key) { if (array_key_exists($key, $normalized)) { $data[$key] = $normalized[$key]; } @@ -437,6 +437,9 @@ class SchemaProvider { } $updated = update_option($option_key, $stored); + if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_captcha_config_reset')) { + aksrt_captcha_config_reset(); + } \AKSRT\Settings\Settings::flush_settings_cache(); if (isset($stored['site_favicon'])) { diff --git a/tools/build-public-assets.mjs b/tools/build-public-assets.mjs index 006e23f2..253c7419 100644 --- a/tools/build-public-assets.mjs +++ b/tools/build-public-assets.mjs @@ -38,6 +38,7 @@ const cssBundles = { 'assets/css/frontend/ajax-spinner.css', 'assets/css/frontend/modal-runtime.css', 'assets/css/frontend/notyf.css', + 'assets/css/shared/captcha.css', ], 'homepage.css': [ 'assets/css/frontend/post-card.css', @@ -88,6 +89,7 @@ const publicScripts = [ 'assets/js/color-extract.js', 'assets/js/effects.js', 'assets/js/runtime/main.min.js', + 'assets/js/captcha-front.js', 'assets/js/instant-page.js', ];