fix(captcha): wire scene switches and rename menu to 人机验证
- Admin: 登录权限 → 人机验证; 停用场景 → 启用场景 (opt-in) - Normalize enabled_scenarios with legacy disabled_scenarios BC - Sign-in: never soft-bypass captcha; cancel unlocks button - Captcha JS: cancel path finishes pending callback as false
This commit is contained in:
@@ -42,10 +42,10 @@ export const permissionsSection = {
|
||||
schemaPage({
|
||||
key: 'permissions-login',
|
||||
name: 'PermissionsLogin',
|
||||
label: '登录权限',
|
||||
label: '人机验证',
|
||||
route: '/permissions/login',
|
||||
title: '登录权限',
|
||||
desc: '登录验证、人机验证、注册限制配置。',
|
||||
title: '人机验证',
|
||||
desc: '云验证码服务商、场景开关与密钥配置。',
|
||||
canonicalDomain: 'permissions',
|
||||
section: 'login',
|
||||
}),
|
||||
|
||||
@@ -115,12 +115,16 @@ async function load() {
|
||||
const comment = commentData?.data ?? commentData ?? {}
|
||||
const captcha = captchaRes?.data ?? captchaRes ?? {}
|
||||
const moderation = moderationRes?.data ?? moderationRes ?? {}
|
||||
const enabledScenarios = Array.isArray(captcha.enabled_scenarios) ? captcha.enabled_scenarios : null
|
||||
const disabledScenarios = Array.isArray(captcha.disabled_scenarios) ? captcha.disabled_scenarios : []
|
||||
const commentSceneOn = enabledScenarios
|
||||
? enabledScenarios.includes('comment')
|
||||
: !disabledScenarios.includes('comment')
|
||||
|
||||
form.comment_require_login = !!comment.comment_require_login
|
||||
form.comment_allow_reply = comment.comment_allow_reply !== false
|
||||
|
||||
status.commentCaptchaEnabled = !!captcha.enabled && !!captcha.active_provider && !disabledScenarios.includes('comment')
|
||||
status.commentCaptchaEnabled = !!captcha.enabled && !!captcha.active_provider && commentSceneOn
|
||||
status.moderationEnabled = !!moderation.enabled
|
||||
status.rateLimitEnabled = !!moderation.rate_limit_enabled
|
||||
status.keywordRuleCount = splitKeywords(moderation.blocked_keywords).length
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
<n-form-item label="下载启用验证码">
|
||||
<n-switch v-model:value="form.download_captcha" />
|
||||
<template #feedback>
|
||||
<span class="aksrt-hint-text">具体验证码服务商与场景白名单仍在“登录权限”页维护。</span>
|
||||
<span class="aksrt-hint-text">具体验证码服务商与场景白名单仍在「人机验证」页维护。</span>
|
||||
</template>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
|
||||
@@ -227,9 +227,16 @@ async function load() {
|
||||
const captchaData = extractData(captchaRes)
|
||||
const dailySignin = creditData.daily_signin || {}
|
||||
const inviteReward = creditData.invite_reward || {}
|
||||
const enabledScenarios = Array.isArray(captchaData.enabled_scenarios)
|
||||
? captchaData.enabled_scenarios
|
||||
: null
|
||||
const disabledScenarios = Array.isArray(captchaData.disabled_scenarios)
|
||||
? captchaData.disabled_scenarios
|
||||
: []
|
||||
const sceneOn = (key) => {
|
||||
if (enabledScenarios) return enabledScenarios.includes(key)
|
||||
return !disabledScenarios.includes(key)
|
||||
}
|
||||
|
||||
creditSnapshot.value = creditData
|
||||
authSnapshot.value = authData
|
||||
@@ -249,8 +256,8 @@ async function load() {
|
||||
form.invite_register_field = authData.invite_register_field || 'optional'
|
||||
form.invite_reject_invalid = authData.invite_reject_invalid !== false
|
||||
|
||||
captchaSummary.register = !!captchaData.enabled && !!captchaData.active_provider && !disabledScenarios.includes('register')
|
||||
captchaSummary.signin = !!captchaData.enabled && !!captchaData.active_provider && !disabledScenarios.includes('signin')
|
||||
captchaSummary.register = !!captchaData.enabled && !!captchaData.active_provider && sceneOn('register')
|
||||
captchaSummary.signin = !!captchaData.enabled && !!captchaData.active_provider && sceneOn('signin')
|
||||
} catch (error) {
|
||||
message.error(error.message || '加载签到奖励配置失败')
|
||||
} finally {
|
||||
|
||||
@@ -9,13 +9,17 @@ export const permissionsSchemas = {
|
||||
*/
|
||||
login: {
|
||||
key: 'permissions.login',
|
||||
title: '登录权限',
|
||||
title: '人机验证',
|
||||
icon: 'fas fa-shield-alt',
|
||||
description: '管理登录、注册、评论等场景的人机验证与访问限制。',
|
||||
description: '配置云验证码服务商,并选择需要强制人机验证的前端场景。',
|
||||
defaults: {
|
||||
enabled: false,
|
||||
active_provider: '',
|
||||
mode: 'always',
|
||||
enabled_scenarios: [
|
||||
'comment', 'login', 'register', 'bind', 'bind_phone',
|
||||
'lostpassword', 'change_password', 'change_email', 'verify_email', 'download', 'signin',
|
||||
],
|
||||
disabled_scenarios: [],
|
||||
tencent: {},
|
||||
aliyun: {},
|
||||
@@ -28,7 +32,7 @@ export const permissionsSchemas = {
|
||||
title: '基础设置',
|
||||
icon: 'fas fa-cog',
|
||||
fields: [
|
||||
{ key: 'enabled', label: '启用人机验证', type: 'switch', help: '开启后将在指定场景要求用户完成人机验证' },
|
||||
{ key: 'enabled', label: '启用人机验证', type: 'switch', help: '总开关:关闭后所有场景均不验证' },
|
||||
{
|
||||
key: 'active_provider',
|
||||
label: '当前服务商',
|
||||
@@ -54,10 +58,10 @@ export const permissionsSchemas = {
|
||||
dependsOn: { key: 'enabled', value: true },
|
||||
},
|
||||
{
|
||||
key: 'disabled_scenarios',
|
||||
label: '停用场景',
|
||||
key: 'enabled_scenarios',
|
||||
label: '启用场景',
|
||||
type: 'checkbox',
|
||||
help: '勾选后表示该场景不启用人机验证;未勾选的场景会启用。',
|
||||
help: '勾选 = 该场景需要人机验证。取消勾选则该场景放行。',
|
||||
dependsOn: { key: 'enabled', value: true },
|
||||
options: [
|
||||
{ label: '发表评论', value: 'comment' },
|
||||
@@ -297,7 +301,7 @@ export const permissionsSchemas = {
|
||||
key: 'permissions.download',
|
||||
title: '下载权限',
|
||||
icon: 'fas fa-download',
|
||||
description: '下载门槛由 DownloadPermissions 页维护(cms_features);人机验证服务商在登录权限页。',
|
||||
description: '下载门槛由 DownloadPermissions 页维护(cms_features);人机验证服务商在「人机验证」页。',
|
||||
defaults: {},
|
||||
groups: [],
|
||||
},
|
||||
|
||||
+22
-14
@@ -114,14 +114,28 @@
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
function triggerCaptcha(callback) {
|
||||
function triggerCaptcha(onSuccess, onFail) {
|
||||
var mount = document.querySelector('[data-signin-captcha]');
|
||||
var state = window.AKSRTCaptchaState;
|
||||
var scene = state && state.scenes && state.scenes.signin;
|
||||
|
||||
if (scene && typeof scene.verify === 'function') {
|
||||
scene.verify(callback);
|
||||
scene.verify(function (ok) {
|
||||
if (ok === false) {
|
||||
if (typeof onFail === 'function') onFail('已取消验证');
|
||||
return;
|
||||
}
|
||||
onSuccess();
|
||||
});
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
|
||||
if (mount) {
|
||||
if (typeof onFail === 'function') onFail('人机验证加载中,请稍候再试');
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
@@ -129,19 +143,13 @@
|
||||
btn.innerHTML = '…';
|
||||
setTip('');
|
||||
|
||||
var captchaDone = false;
|
||||
var timeoutId = setTimeout(function () {
|
||||
if (!captchaDone) {
|
||||
captchaDone = true;
|
||||
doSignin();
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
triggerCaptcha(function () {
|
||||
clearTimeout(timeoutId);
|
||||
if (captchaDone) return;
|
||||
captchaDone = true;
|
||||
doSignin();
|
||||
}, function (msg) {
|
||||
resetCaptcha();
|
||||
if (msg) setTip(msg);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '签到';
|
||||
});
|
||||
|
||||
function doSignin() {
|
||||
|
||||
Reference in New Issue
Block a user