Merge pull request 'fix(captcha): close human-verification loop end-to-end' (#108) from worktree-captcha-wire-through into main

Reviewed-on: CYL/wordpress-theme-aksrt#108
This commit is contained in:
2026-07-13 20:23:34 +08:00
21 changed files with 1163 additions and 1515 deletions
@@ -120,7 +120,7 @@ async function load() {
form.comment_require_login = !!comment.comment_require_login
form.comment_allow_reply = comment.comment_allow_reply !== false
status.commentCaptchaEnabled = !!captcha.enabled && !disabledScenarios.includes('comment')
status.commentCaptchaEnabled = !!captcha.enabled && !!captcha.active_provider && !disabledScenarios.includes('comment')
status.moderationEnabled = !!moderation.enabled
status.rateLimitEnabled = !!moderation.rate_limit_enabled
status.keywordRuleCount = splitKeywords(moderation.blocked_keywords).length
@@ -289,29 +289,15 @@ export const permissionsSchemas = {
},
/**
* Download permissions
* Download permissions — live UI is DownloadPermissions.vue via cms_features.
* Keep empty schema so SchemaPage cannot mis-write aksrt_settings.
*/
download: {
key: 'permissions.download',
title: '下载权限',
icon: 'fas fa-download',
description: '控制资源下载的权限和限制。',
defaults: {
download_require_login: false,
download_daily_limit: 0,
download_captcha: false,
},
groups: [
{
key: 'basic',
title: '下载设置',
icon: 'fas fa-cog',
fields: [
{ key: 'download_require_login', label: '需登录下载', type: 'switch' },
{ key: 'download_daily_limit', label: '每日下载限制', type: 'number', min: 0, help: '0 表示不限制' },
{ key: 'download_captcha', label: '下载验证码', type: 'switch', help: '下载前需要验证码' },
],
},
],
description: '下载门槛由 DownloadPermissions 页维护(cms_features);人机验证服务商在登录权限页。',
defaults: {},
groups: [],
},
};
+5 -2
View File
@@ -231,8 +231,11 @@
const state = window.AKSRTCaptchaState;
const scene = state && state.scenes && state.scenes.download;
if (!scene || typeof scene.verify !== 'function') {
// captcha 组件还未就绪,直接发,由后端兜底报错
callback();
// captcha 组件未就绪:不直发,避免绕过服务端门槛
if (window.console && console.warn) {
console.warn('[AKSRT] download captcha not ready');
}
alert(tt('captcha_loading', '验证码组件加载中,请稍候再试'));
return;
}
scene.verify(function () { callback(); });
+17 -3
View File
@@ -232,9 +232,15 @@
var replyInfo = document.getElementById('comment-reply-info');
if (replyInfo) replyInfo.style.display = 'none';
// Reset captcha
var captchaImg = form.querySelector('.imagecaptcha img');
if (captchaImg) captchaImg.click();
// Reset captcha after successful submit
try {
var captchaScene = window.AKSRTCaptchaState &&
window.AKSRTCaptchaState.scenes &&
window.AKSRTCaptchaState.scenes.comment;
if (captchaScene && typeof captchaScene.reset === 'function') {
captchaScene.reset();
}
} catch (e) { /* ignore */ }
// Wait time countdown
if (json.wait_time && json.wait_time > 0) {
@@ -251,6 +257,14 @@
if (json && json.code) {
showToast(json.message || '评论提交失败', 'error');
try {
var failScene = window.AKSRTCaptchaState &&
window.AKSRTCaptchaState.scenes &&
window.AKSRTCaptchaState.scenes.comment;
if (failScene && typeof failScene.reset === 'function') {
failScene.reset();
}
} catch (e) { /* ignore */ }
if (json.wait_time) startCountdown(json.wait_time);
else setLoading(false);
return;
+105 -64
View File
@@ -163,9 +163,33 @@
clearMessages();
}
function withCaptcha(scene, form, next) {
var store = window.AKSRTCaptchaState && window.AKSRTCaptchaState.scenes
? window.AKSRTCaptchaState.scenes
: null;
var instance = store && store[scene] ? store[scene] : null;
if (instance && typeof instance.verify === 'function') {
instance.verify(function () {
next(serializeForm(form));
});
return;
}
next(serializeForm(form));
}
function resetCaptcha(scene) {
try {
var store = window.AKSRTCaptchaState && window.AKSRTCaptchaState.scenes
? window.AKSRTCaptchaState.scenes
: null;
if (store && store[scene] && typeof store[scene].reset === 'function') {
store[scene].reset();
}
} catch (e) { /* ignore */ }
}
function handleLogin(form) {
var payload = serializeForm(form);
if (!payload.log || !payload.pwd) {
if (!form.querySelector('[name="log"]') || !form.querySelector('[name="pwd"]')) {
showMessage('login', '请输入用户名和密码', 'error');
return;
}
@@ -173,76 +197,93 @@
setLoading(form, true);
clearMessages();
request('/auth/modal-login', payload)
.then(function (result) {
if (result.ok && result.data.success) {
onAuthSuccess(result.data);
return;
}
var message = result.data.message || '登录失败';
if (result.data.verify_url) {
message += ' <a href="' + escapeAttribute(result.data.verify_url) + '">前往验证</a>';
}
showMessage('login', message, 'error');
})
.catch(function () {
showMessage('login', '网络错误,请稍后重试', 'error');
})
.finally(function () {
withCaptcha('login', form, function (payload) {
if (!payload.log || !payload.pwd) {
setLoading(form, false);
});
showMessage('login', '请输入用户名和密码', 'error');
return;
}
request('/auth/modal-login', payload)
.then(function (result) {
if (result.ok && result.data.success) {
onAuthSuccess(result.data);
return;
}
resetCaptcha('login');
var message = result.data.message || '登录失败';
if (result.data.verify_url) {
message += ' <a href="' + escapeAttribute(result.data.verify_url) + '">前往验证</a>';
}
showMessage('login', message, 'error');
})
.catch(function () {
resetCaptcha('login');
showMessage('login', '网络错误,请稍后重试', 'error');
})
.finally(function () {
setLoading(form, false);
});
});
}
function handleRegister(form) {
var payload = serializeForm(form);
if (!payload.user_login || !payload.user_email || !payload.user_pass || !payload.user_pass_confirm) {
showMessage('register', '请完整填写注册信息', 'error');
return;
}
if (payload.user_pass !== payload.user_pass_confirm) {
showMessage('register', '两次输入的密码不一致', 'error');
return;
}
setLoading(form, true);
clearMessages();
request('/auth/modal-register', payload)
.then(function (result) {
if (!result.ok || !result.data.success) {
showMessage('register', result.data.message || '注册失败', 'error');
return;
}
if (result.data.user) {
onAuthSuccess(result.data);
return;
}
if (result.data.verification_required) {
var verifyMessage = result.data.message || '注册成功,请完成邮箱验证';
if (result.data.verify_url) {
verifyMessage += ' <a href="' + escapeAttribute(result.data.verify_url) + '">前往验证</a>';
}
showMessage('register', verifyMessage, 'success');
form.reset();
return;
}
var loginMessage = result.data.message || '注册成功,请登录';
switchTab('login');
prefillLogin(result.data.user_login || result.data.user_email || '');
showMessage('login', loginMessage, 'success');
form.reset();
})
.catch(function () {
showMessage('register', '网络错误,请稍后重试', 'error');
})
.finally(function () {
withCaptcha('register', form, function (payload) {
if (!payload.user_login || !payload.user_email || !payload.user_pass || !payload.user_pass_confirm) {
setLoading(form, false);
});
showMessage('register', '请完整填写注册信息', 'error');
return;
}
if (payload.user_pass !== payload.user_pass_confirm) {
setLoading(form, false);
showMessage('register', '两次输入的密码不一致', 'error');
return;
}
request('/auth/modal-register', payload)
.then(function (result) {
if (!result.ok || !result.data.success) {
resetCaptcha('register');
showMessage('register', result.data.message || '注册失败', 'error');
return;
}
if (result.data.user) {
onAuthSuccess(result.data);
return;
}
if (result.data.verification_required) {
var verifyMessage = result.data.message || '注册成功,请完成邮箱验证';
if (result.data.verify_url) {
verifyMessage += ' <a href="' + escapeAttribute(result.data.verify_url) + '">前往验证</a>';
}
showMessage('register', verifyMessage, 'success');
form.reset();
resetCaptcha('register');
return;
}
var loginMessage = result.data.message || '注册成功,请登录';
switchTab('login');
prefillLogin(result.data.user_login || result.data.user_email || '');
showMessage('login', loginMessage, 'success');
form.reset();
resetCaptcha('register');
})
.catch(function () {
resetCaptcha('register');
showMessage('register', '网络错误,请稍后重试', 'error');
})
.finally(function () {
setLoading(form, false);
});
});
}
function serializeForm(form) {
+44 -24
View File
@@ -81,11 +81,12 @@
// 收集人机验证字段
const form = btn.closest('form')
const captchaData = collectCaptchaFields(form)
const scene = btn.dataset.aksrtSmsScene || 'register'
fetch(REST_BASE + '/auth/send-sms-code', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, ...captchaData }),
body: JSON.stringify({ phone, scene, ...captchaData }),
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
@@ -97,6 +98,12 @@
} else {
const msg = (data.data && data.data.message) || data.message || '发送失败'
showToast(msg, 'error')
try {
const captchaState = window.AKSRTCaptchaState
if (captchaState && captchaState.scenes && captchaState.scenes[scene]) {
captchaState.scenes[scene].reset()
}
} catch (e) { /* ignore */ }
const remaining = data.data && data.data.remaining_time
if (remaining && remaining > 0) {
sessionStorage.setItem(COUNTDOWN_KEY, String(Date.now() + remaining * 1000))
@@ -265,33 +272,46 @@
body = { phone, password }
}
fetch(REST_BASE + endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
if (ok && data.success) {
showToast('登录成功', 'success')
setTimeout(() => {
window.location.href = data.data?.redirect || '/'
}, 500)
} else {
showToast(data.message || '登录失败', 'error')
const captchaState = window.AKSRTCaptchaState
const doLogin = () => {
const captchaData = collectCaptchaFields(form)
fetch(REST_BASE + endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ...body, ...captchaData }),
})
.then(r => r.json().then(data => ({ ok: r.ok, data })))
.then(({ ok, data }) => {
if (ok && data.success) {
showToast('登录成功', 'success')
setTimeout(() => {
window.location.href = data.data?.redirect || '/'
}, 500)
} else {
showToast(data.message || '登录失败', 'error')
if (captchaState && captchaState.scenes && captchaState.scenes.login) {
captchaState.scenes.login.reset()
}
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '登录'
}
}
})
.catch(() => {
showToast('网络错误', 'error')
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '登录'
}
}
})
.catch(() => {
showToast('网络错误', 'error')
if (submitBtn) {
submitBtn.disabled = false
submitBtn.textContent = '登录'
}
})
})
}
if (captchaState && captchaState.scenes && captchaState.scenes.login) {
captchaState.scenes.login.verify(doLogin)
} else {
doLogin()
}
}
/* =================================================================
@@ -173,8 +173,12 @@ class AKSRT_Admin_Extension_Controller extends AKSRT_Admin_Controller_Base {
$email_gate_enabled = false;
}
$captcha_comment_enabled = !empty($settings['captcha_comment_enabled']);
$captcha_download_enabled = !empty($settings['captcha_download_enabled']);
$captcha_summary = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'configured' => false, 'scenes' => []];
$captcha_comment_enabled = !empty($captcha_summary['scenes']['comment']);
$features = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_cms_features() : [];
$captcha_download_enabled = !empty($features['download_captcha']) && !empty($captcha_summary['scenes']['download']);
$risk_switches = [
'show_unapproved_comments' => !empty($settings['show_unapproved_comments']),
@@ -191,7 +195,8 @@ class AKSRT_Admin_Extension_Controller extends AKSRT_Admin_Controller_Base {
'content_audit_configured' => !empty($services['audit']['configured']),
'ip_geo_enabled' => !empty($services['ip_geo']['enabled']),
'ip_geo_configured' => !empty($services['ip_geo']['configured']),
'captcha_enabled' => $captcha_comment_enabled || $captcha_download_enabled,
'captcha_enabled' => !empty($captcha_summary['enabled']) && !empty($captcha_summary['configured']),
'captcha_summary' => $captcha_summary,
'risk_switches' => $risk_switches,
'services' => $services,
],
@@ -452,17 +457,19 @@ class AKSRT_Admin_Extension_Controller extends AKSRT_Admin_Controller_Base {
$services['wechat'] = ['enabled' => false, 'provider' => '', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
}
// Captcha
$settings = AKSRT_Settings::get_all();
$captcha_en = class_exists('AKSRT_Captcha') && !empty($settings['captcha_enabled']);
// Captcha — 唯一真相源 aksrt_captcha_config
$captcha = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'provider' => '', 'configured' => false, 'scenes' => []];
$services['captcha'] = [
'enabled' => $captcha_en,
'provider' => sanitize_key((string) ($settings['captcha_provider'] ?? '')),
'configured' => $captcha_en,
'available' => $captcha_en,
'enabled' => !empty($captcha['enabled']) && !empty($captcha['configured']),
'provider' => (string) ($captcha['provider'] ?? ''),
'configured' => !empty($captcha['configured']),
'available' => !empty($captcha['configured']),
'verified' => false,
'transport' => 'http_api',
'missing_fields' => [],
'scenes' => $captcha['scenes'] ?? [],
];
// IP Geo
@@ -181,6 +181,17 @@ trait AKSRT_Admin_User_Controller_Auth_Phone_Operations {
public function auth_send_sms_code(WP_REST_Request $request): WP_REST_Response {
$params = $this->get_json_params($request);
$phone = sanitize_text_field((string) ($params['phone'] ?? ''));
$scene = sanitize_key((string) ($params['scene'] ?? 'register'));
if (!array_key_exists($scene, function_exists('aksrt_captcha_scenes') ? aksrt_captcha_scenes() : [])) {
$scene = 'register';
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha($scene, is_array($params) ? $params : []);
if ($captcha_result !== true) {
return $this->error((string) $captcha_result, 400);
}
}
if (!class_exists('AKSRT_SMS')) {
return $this->error('短信服务不可用');
@@ -52,28 +52,8 @@ class AKSRT_Admin_Schema_Extensions_Security {
['key' => 'time_format', 'label' => '时间显示格式', 'type' => 'input', 'default' => 'n月j日 H:i'],
['key' => 'remove_more_wp_head', 'label' => '移除WP head多余代码', 'type' => 'switch', 'default' => true],
]],
['key' => 'verification', 'title' => '验证设置', 'fields' => [
['key' => 'verification_comment_s', 'label' => '评论需要验证', 'type' => 'switch', 'default' => false],
['key' => 'verification_links_s', 'label' => '链接需要验证', 'type' => 'switch', 'default' => true],
['key' => 'verification_newposts_s', 'label' => '新文章需要验证', 'type' => 'switch', 'default' => false],
['key' => 'verification_bbspost_s', 'label' => '论坛帖子需要验证', 'type' => 'switch', 'default' => false],
['key' => 'verification_signin_exclude', 'label' => '登录人机验证排除账号', 'type' => 'input', 'default' => ''],
['key' => 'user_verification_type', 'label' => '验证类型', 'type' => 'radio', 'default' => 'slider', 'options' => [
['label' => '图形验证码', 'value' => 'image'],
['label' => '滑块验证', 'value' => 'slider'],
['label' => '腾讯验证码', 'value' => 'tcaptcha'],
['label' => '极验验证码', 'value' => 'geetest'],
['label' => '关闭验证', 'value' => 'null'],
]],
]],
['key' => 'captcha', 'title' => '验证码设置', 'fields' => [
['key' => 'geetest_enabled', 'label' => '极验验证码', 'type' => 'switch', 'default' => false],
['key' => 'geetest_id', 'label' => '极验 ID', 'type' => 'input', 'default' => ''],
['key' => 'geetest_key', 'label' => '极验 Key', 'type' => 'input', 'default' => ''],
['key' => 'tcaptcha_enabled', 'label' => '腾讯云验证码', 'type' => 'switch', 'default' => false],
['key' => 'tcaptcha_appid', 'label' => '腾讯云 AppID', 'type' => 'input', 'default' => ''],
['key' => 'tcaptcha_secret_id', 'label' => '腾讯云 SecretId', 'type' => 'input', 'default' => ''],
['key' => 'tcaptcha_secret_key', 'label' => '腾讯云 SecretKey', 'type' => 'input', 'default' => ''],
['key' => 'captcha-note', 'title' => '人机验证', 'fields' => [
['key' => 'captcha_redirect_hint', 'label' => '配置入口', 'type' => 'notice', 'alertType' => 'info', 'alertContent' => '人机验证(腾讯云 / 阿里云 / Cloudflare / 极验)请到「功能&权限 → 登录权限」统一配置;本页不再维护验证码字段。'],
]],
['key' => 'rate-limit', 'title' => '频率限制', 'fields' => [
['key' => 'brush_limit_post_enabled', 'label' => '文章发布限制', 'type' => 'switch', 'default' => false],
@@ -137,7 +137,7 @@ class AKSRT_Admin_Schema_Permissions {
['key' => 'download_role_size', 'label' => '角色文件大小限制', 'type' => 'textarea', 'default' => '', 'help' => '格式: role|max_size_mb,每行一条'],
]],
['key' => 'captcha', 'title' => '安全设置', 'fields' => [
['key' => 'download_captcha', 'label' => '下载需要验证码', 'type' => 'switch', 'default' => false],
['key' => 'download_captcha_hint', 'label' => '下载验证码', 'type' => 'notice', 'alertType' => 'info', 'alertContent' => '下载验证码门槛在「功能&权限 → 下载权限」页(cms_features.download_captcha)维护;服务商与场景在「登录权限」。'],
['key' => 'download_ip_limit', 'label' => 'IP下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'help' => '同一IP每小时最大下载次数,0为不限制'],
]],
],
@@ -47,12 +47,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => $password_check];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('register')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('register', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -154,12 +152,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('bind')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('bind', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -371,12 +367,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('verify_email')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('verify_email', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -417,12 +411,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => __('该账号已完成邮箱验证,无需重复发送', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('verify_email')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('verify_email', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -110,12 +110,10 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('lostpassword')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('lostpassword', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -247,6 +245,13 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('密码至少 6 位', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('register', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
// 验证 SMS 验证码
if (class_exists('AKSRT_SMS')) {
$verify_result = AKSRT_SMS::get_instance()->verify_code($phone, $code);
@@ -321,6 +326,13 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('手机号格式不正确', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('login', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
$user = self::get_user_by_phone($phone);
if (!$user) {
return ['success' => false, 'error' => __('该手机号未注册', 'aksrt')];
@@ -359,6 +371,13 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('手机号格式不正确', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('login', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
// 验证 SMS 验证码
if (class_exists('AKSRT_SMS')) {
$verify_result = AKSRT_SMS::get_instance()->verify_code($phone, $code);
+7
View File
@@ -368,6 +368,13 @@ final class AKSRT_Auth_Service {
}
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('login', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
$creds = [
'user_login' => $resolved_login,
'user_password' => (string) ($post['pwd'] ?? ''),
File diff suppressed because it is too large Load Diff
+11 -5
View File
@@ -227,6 +227,17 @@ class AKSRT_Comment_Actions {
], 403);
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('comment', $params);
if ($captcha_result !== true) {
return new \WP_REST_Response([
'success' => false,
'code' => 'captcha_failed',
'message' => (string) $captcha_result,
], 400);
}
}
$user = wp_get_current_user();
$commentdata = [
'comment_post_ID' => $post_id,
@@ -263,11 +274,6 @@ class AKSRT_Comment_Actions {
$_POST[$k] = $params[$k];
}
}
foreach ($params as $k => $v) {
if (is_string($k) && (str_contains($k, 'captcha') || str_starts_with($k, 'g-recaptcha') || str_starts_with($k, 'cf-turnstile') || str_starts_with($k, 'h-captcha'))) {
$_POST[$k] = $v;
}
}
$comment_id = wp_new_comment(wp_slash($commentdata), true);
if (is_wp_error($comment_id)) {
+10 -9
View File
@@ -162,16 +162,17 @@ class AKSRT_Download {
if (is_wp_error($gate)) return $gate;
$paid_settings = AKSRT_Settings::get_paid_content_settings();
$need_captcha = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
if ($need_captcha && function_exists('aksrt_is_captcha_enabled_for') && !aksrt_is_captcha_enabled_for('download')) {
$need_captcha = false;
}
if ($need_captcha && function_exists('aksrt_verify_captcha')) {
$need_captcha = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
// 下载双开关:cms_features.download_captcha(或单资源强制)且场景未停用。
if ($need_captcha && function_exists('aksrt_require_captcha')) {
$cap_payload = isset($params['captcha']) && is_array($params['captcha']) ? $params['captcha'] : [];
$verify = aksrt_verify_captcha($cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
// 场景未启用时 require 直接放行;启用时强制校验 token。
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download')) {
$verify = aksrt_require_captcha('download', $cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
}
}
}
+9 -9
View File
@@ -95,15 +95,15 @@ class AKSRT_Signin {
}
// 人机验证(若启用)
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('signin')) {
$body = $request->get_json_params();
$cap_payload = isset($body['captcha']) && is_array($body['captcha']) ? $body['captcha'] : [];
if (function_exists('aksrt_verify_captcha')) {
$verify = aksrt_verify_captcha($cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
}
if (function_exists('aksrt_require_captcha')) {
$body = $request->get_json_params();
$cap_payload = is_array($body) && isset($body['captcha']) && is_array($body['captcha'])
? $body['captcha']
: (is_array($body) ? $body : []);
$verify = aksrt_require_captcha('signin', $cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
}
}
+9 -4
View File
@@ -296,10 +296,15 @@ class AKSRT_SMS {
wp_send_json_error(['message' => __('手机号格式不正确', 'aksrt')]);
}
// 人机验证钩子(对齐 zibll 的 man_machine_verification)
$captcha_result = apply_filters('aksrt_sms_captcha_verify', true, $phone);
if ($captcha_result !== true) {
wp_send_json_error(['message' => $captcha_result ?: __('人机验证失败', 'aksrt')]);
$scene = sanitize_key((string) ($_POST['scene'] ?? 'register'));
if (!array_key_exists($scene, function_exists('aksrt_captcha_scenes') ? aksrt_captcha_scenes() : [])) {
$scene = 'register';
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha($scene, wp_unslash($_POST));
if ($captcha_result !== true) {
wp_send_json_error(['message' => $captcha_result ?: __('人机验证失败', 'aksrt')]);
}
}
$result = $this->send_code($phone);
+6 -6
View File
@@ -1004,8 +1004,8 @@ add_action('wp_ajax_aksrt_send_email_code', function () {
wp_send_json_error( array( 'message' => __( '无效的验证场景', 'aksrt' ) ) );
}
if ( aksrt_is_captcha_enabled_for( $scene ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( $scene, wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
@@ -1113,8 +1113,8 @@ add_action('wp_ajax_aksrt_change_password', function () {
}
// 人机验证
if ( aksrt_is_captcha_enabled_for( 'change_password' ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( 'change_password', wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
@@ -1178,8 +1178,8 @@ add_action('wp_ajax_aksrt_change_email', function () {
}
// 人机验证
if ( aksrt_is_captcha_enabled_for( 'change_email' ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( 'change_email', wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
@@ -886,18 +886,6 @@ trait AKSRT_Scripts_Part_1b {
);
}
// Slider CAPTCHA (canvas-based puzzle verification)
$slider_captcha_js = get_template_directory() . '/assets/js/slider-captcha.js';
if (file_exists($slider_captcha_js)) {
wp_enqueue_script(
'aksrt-slider-captcha',
$base_uri . '/assets/js/slider-captcha.js',
[],
(string) filemtime($slider_captcha_js),
true
);
}
// Instant page prefetch (hover to preload)
$instant_page_js = get_template_directory() . '/assets/js/instant-page.js';
if (file_exists($instant_page_js)) {
@@ -182,63 +182,47 @@ class AKSRT_Settings_Capabilities {
}
/**
* 获取"网站安全"设置
* 获取"网站安全"设置(人机验证见 aksrt_get_captcha_status_summary)。
*/
public static function get_security_settings(): array {
$s = AKSRT_Settings::get_all();
$verification_type = sanitize_key((string) ($s['user_verification_type'] ?? 'slider'));
$allowed_types = ['image', 'slider', 'tcaptcha', 'geetest', 'null'];
if (!in_array($verification_type, $allowed_types, true)) {
$verification_type = 'slider';
}
$captcha = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'provider' => '', 'configured' => false, 'scenes' => []];
return [
'verification_type' => $verification_type,
'verification_comment_s' => !empty($s['verification_comment_s']),
'verification_newposts_s' => !empty($s['verification_newposts_s']),
'verification_bbspost_s' => !empty($s['verification_bbspost_s']),
'verification_links_s' => !empty($s['verification_links_s'] ?? true),
'verification_signin_exclude' => sanitize_text_field((string) ($s['verification_signin_exclude'] ?? '')),
'geetest_enabled' => !empty($s['geetest_enabled']),
'geetest_id' => sanitize_text_field((string) ($s['geetest_id'] ?? '')),
'geetest_key' => sanitize_text_field((string) ($s['geetest_key'] ?? '')),
'tcaptcha_enabled' => !empty($s['tcaptcha_enabled']),
'tcaptcha_appid' => sanitize_text_field((string) ($s['tcaptcha_appid'] ?? '')),
'tcaptcha_secret_key' => sanitize_text_field((string) ($s['tcaptcha_secret_key'] ?? '')),
'brush_limit_post' => [
'captcha' => $captcha,
'brush_limit_post' => [
'enabled' => !empty($s['brush_limit_post_enabled']),
'minutes_10' => (int) ($s['brush_limit_post_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_post_day_1'] ?? 50),
],
'brush_limit_comment' => [
'brush_limit_comment' => [
'enabled' => !empty($s['brush_limit_comment_enabled']),
'minutes_10' => (int) ($s['brush_limit_comment_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_comment_day_1'] ?? 100),
],
'brush_limit_upload' => [
'brush_limit_upload' => [
'enabled' => !empty($s['brush_limit_upload_enabled']),
'minutes_10' => (int) ($s['brush_limit_upload_minutes_10'] ?? 50),
'day_1' => (int) ($s['brush_limit_upload_day_1'] ?? 200),
],
'brush_limit_register' => [
'brush_limit_register' => [
'enabled' => !empty($s['brush_limit_register_enabled']),
'minutes_10' => (int) ($s['brush_limit_register_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_register_day_1'] ?? 100),
],
'brush_limit_order' => [
'brush_limit_order' => [
'enabled' => !empty($s['brush_limit_order_enabled']),
'minutes_10' => (int) ($s['brush_limit_order_minutes_10'] ?? 30),
'day_1' => (int) ($s['brush_limit_order_day_1'] ?? 100),
],
'error_frequency_limit' => !empty($s['error_frequency_limit'] ?? true),
'user_disable_admin' => !empty($s['user_disable_admin'] ?? true),
'disabled_pingback' => !empty($s['disabled_pingback'] ?? true),
'disabled_xmlrpc' => !empty($s['disabled_xmlrpc'] ?? true),
'close_attachment_page' => !empty($s['close_attachment_page'] ?? true),
'error_frequency_limit' => !empty($s['error_frequency_limit'] ?? true),
];
}
/**
* 获取"系统工具"设置
*/