diff --git a/inc/classes/admin/class-aksrt-admin-schema-provider.php b/inc/classes/admin/class-aksrt-admin-schema-provider.php
index bbda23dd..555cb6e1 100644
--- a/inc/classes/admin/class-aksrt-admin-schema-provider.php
+++ b/inc/classes/admin/class-aksrt-admin-schema-provider.php
@@ -365,7 +365,24 @@ class AKSRT_Admin_Schema_Provider {
case 'float':
return (float) $value;
case 'array':
- return is_array($value) ? $value : [];
+ // 深层字符串净化,保留嵌套服务商配置(如 geetest.captcha_id)
+ if (!is_array($value)) {
+ return [];
+ }
+ $out = [];
+ foreach ($value as $k => $v) {
+ $key = is_string($k) ? sanitize_key($k) : $k;
+ if (is_array($v)) {
+ $out[$key] = $this->sanitize_value($v, 'array');
+ } elseif (is_bool($v)) {
+ $out[$key] = $v;
+ } elseif (is_int($v) || is_float($v)) {
+ $out[$key] = $v;
+ } else {
+ $out[$key] = sanitize_text_field((string) $v);
+ }
+ }
+ return $out;
default:
return sanitize_text_field((string) $value);
}
@@ -383,6 +400,8 @@ class AKSRT_Admin_Schema_Provider {
'radio' => 'string',
'checkbox' => 'array',
'repeater' => 'array',
+ // 自定义面板(验证码服务商等)按对象存储
+ 'custom' => 'array',
'color' => 'string',
'icon' => 'string',
'media' => 'url',
diff --git a/inc/classes/class-aksrt-captcha.php b/inc/classes/class-aksrt-captcha.php
index 08500b42..83735127 100644
--- a/inc/classes/class-aksrt-captcha.php
+++ b/inc/classes/class-aksrt-captcha.php
@@ -21,6 +21,35 @@ function aksrt_get_captcha_config() {
}
endif;
+if (!function_exists('aksrt_captcha_provider_is_configured')):
+/**
+ * 当前服务商必填凭据是否齐全(避免空 captcha_id 打到极验 403)。
+ *
+ * @param string $provider
+ * @param array $pcfg
+ * @return bool
+ */
+function aksrt_captcha_provider_is_configured($provider, $pcfg = array()) {
+ $provider = sanitize_key((string) $provider);
+ if ($provider === '') {
+ return false;
+ }
+ if (!is_array($pcfg)) {
+ $pcfg = array();
+ }
+ $required = aksrt_captcha_required_fields($provider);
+ if (empty($required)) {
+ return false;
+ }
+ foreach ($required as $field) {
+ if (!isset($pcfg[$field]) || trim((string) $pcfg[$field]) === '') {
+ return false;
+ }
+ }
+ return true;
+}
+endif;
+
if (!function_exists('aksrt_is_captcha_enabled_for')):
/**
* 检查指定场景是否启用人机验证
@@ -32,8 +61,14 @@ function aksrt_is_captcha_enabled_for($scene) {
$cfg = aksrt_get_captcha_config();
if (empty($cfg['enabled'])) return false;
if (empty($cfg['active_provider'])) return false;
+ $provider = (string) $cfg['active_provider'];
+ $pcfg = isset($cfg[$provider]) && is_array($cfg[$provider]) ? $cfg[$provider] : array();
+ // 启用但凭据未配齐时,前台不渲染/不拦截,避免空 captcha_id 请求第三方 403
+ if (!aksrt_captcha_provider_is_configured($provider, $pcfg)) {
+ return false;
+ }
$disabled = isset($cfg['disabled_scenarios']) ? $cfg['disabled_scenarios'] : array();
- return !in_array($scene, $disabled);
+ return !in_array($scene, $disabled, true);
}
endif;
@@ -516,6 +551,18 @@ function aksrt_render_captcha($scene = 'comment') {
if (!aksrt_is_captcha_enabled_for($scene)) return;
$provider = $cfg['active_provider'];
$pcfg = isset($cfg[$provider]) && is_array($cfg[$provider]) ? $cfg[$provider] : array();
+ if (!aksrt_captcha_provider_is_configured($provider, $pcfg)) {
+ if (current_user_can('manage_options')) {
+ echo '
';
+ echo esc_html(sprintf(
+ /* translators: %s: captcha provider key */
+ __('人机验证服务商「%s」已启用但凭据未配齐,前台已跳过渲染。请到「功能&权限 → 登录权限」补全配置。', 'aksrt'),
+ $provider
+ ));
+ echo '
';
+ }
+ return;
+ }
$uid = 'aksrt-captcha-' . $scene . '-' . wp_unique_id();
$vid = 'captcha_verified_' . $scene . '_' . wp_unique_id();
@@ -526,20 +573,20 @@ function aksrt_render_captcha($scene = 'comment') {
'scene' => $scene,
'uid' => $uid,
'vid' => $vid,
- 'tc' => array('app_id' => isset($pcfg['app_id']) ? $pcfg['app_id'] : ''),
+ 'tc' => array('app_id' => isset($pcfg['app_id']) ? (string) $pcfg['app_id'] : ''),
'cf' => array(
- 'site_key' => isset($pcfg['site_key']) ? $pcfg['site_key'] : '',
- 'theme' => isset($pcfg['theme']) ? $pcfg['theme'] : 'auto',
- 'size' => isset($pcfg['size']) ? $pcfg['size'] : 'normal'
+ 'site_key' => isset($pcfg['site_key']) ? (string) $pcfg['site_key'] : '',
+ 'theme' => isset($pcfg['theme']) ? (string) $pcfg['theme'] : 'auto',
+ 'size' => isset($pcfg['size']) ? (string) $pcfg['size'] : 'normal'
),
'gt' => array(
- 'captcha_id' => isset($pcfg['captcha_id']) ? $pcfg['captcha_id'] : '',
- 'product' => isset($pcfg['product']) ? $pcfg['product'] : 'popup'
+ 'captcha_id' => isset($pcfg['captcha_id']) ? trim((string) $pcfg['captcha_id']) : '',
+ 'product' => isset($pcfg['product']) && $pcfg['product'] !== '' ? (string) $pcfg['product'] : 'bind',
),
'al' => array(
- 'region' => isset($pcfg['region']) ? $pcfg['region'] : 'cn',
- 'prefix' => isset($pcfg['prefix']) ? $pcfg['prefix'] : '',
- 'scene_id' => isset($pcfg['scene_id']) ? $pcfg['scene_id'] : ''
+ 'region' => isset($pcfg['region']) ? (string) $pcfg['region'] : 'cn',
+ 'prefix' => isset($pcfg['prefix']) ? (string) $pcfg['prefix'] : '',
+ 'scene_id' => isset($pcfg['scene_id']) ? (string) $pcfg['scene_id'] : ''
),
);
?>
@@ -921,10 +968,17 @@ function init(){
// ============ 极验 GeeTest v4 ============
function loadGeetest(){
showLoading();
- var product=C.gt.product||'bind';
+ var captchaId=(C.gt && C.gt.captcha_id) ? String(C.gt.captcha_id).trim() : '';
+ var product=(C.gt && C.gt.product) ? String(C.gt.product) : 'bind';
+ if(!captchaId){
+ clearLoading();
+ showTip('极验 Captcha ID 未配置,请到后台「功能&权限 → 登录权限」填写并保存','#e74c3c');
+ triggerCaptcha=function(){ showTip('极验 Captcha ID 未配置','#e74c3c'); };
+ return;
+ }
function render(){
clearLoading();
- initGeetest4({captchaId:C.gt.captcha_id, product:product, hideSuccess:true}, function(capIns){
+ initGeetest4({captchaId:captchaId, product:product, hideSuccess:true}, function(capIns){
window['__gt_'+C.uid]=capIns;
function onSuccess(){
var r=capIns.getValidate();
@@ -1162,10 +1216,19 @@ function init(){
loadGeetest=function(){
showLoading();
- var product=C.gt.product||'bind';
+ var captchaId=(C.gt && C.gt.captcha_id) ? String(C.gt.captcha_id).trim() : '';
+ var product=(C.gt && C.gt.product) ? String(C.gt.product) : 'bind';
+ if(!captchaId){
+ clearLoading();
+ showTip('极验 Captcha ID 未配置,请到后台「功能&权限 → 登录权限」填写并保存', '#e74c3c');
+ triggerCaptcha=function(){
+ showTip('极验 Captcha ID 未配置', '#e74c3c');
+ };
+ return;
+ }
function render(){
clearLoading();
- initGeetest4({captchaId:C.gt.captcha_id, product:product, hideSuccess:true}, function(capIns){
+ initGeetest4({captchaId:captchaId, product:product, hideSuccess:true}, function(capIns){
window['__gt_'+C.uid]=capIns;
function onSuccess(){
var result=capIns.getValidate();