登录注册体系的源码级硬切换
This commit is contained in:
1 parent
73fcd4ed72
commit
af52f9e94d
72 files changed
+1666
-6576
No files matched your search
@@ -324,14 +324,6 @@ class ImportExportController extends ControllerBase {
|
||||
'status' => empty($missing_tables) ? 'ok' : 'error',
|
||||
];
|
||||
|
||||
// Email verification backfill
|
||||
$backfill_done = get_option('aksrt_email_verification_backfill_done', false);
|
||||
$checks['email_backfill'] = [
|
||||
'label' => '邮箱验证迁移',
|
||||
'value' => $backfill_done ? '已完成' : '未完成',
|
||||
'status' => $backfill_done ? 'ok' : 'info',
|
||||
];
|
||||
|
||||
// Option count
|
||||
$option_count = (int) $wpdb->get_var(
|
||||
"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE 'aksrt_%'"
|
||||
|
||||
@@ -32,7 +32,7 @@ class SchemaPermissions {
|
||||
['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', 'verify_email', 'download', 'signin'], 'dependsOn' => ['key' => 'enabled', 'value' => true], 'options' => [
|
||||
['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'],
|
||||
['label' => '用户注册', 'value' => 'register'],
|
||||
@@ -41,7 +41,6 @@ class SchemaPermissions {
|
||||
['label' => '找回密码', 'value' => 'lostpassword'],
|
||||
['label' => '修改密码', 'value' => 'change_password'],
|
||||
['label' => '更改邮箱', 'value' => 'change_email'],
|
||||
['label' => '邮箱验证', 'value' => 'verify_email'],
|
||||
['label' => '资源下载', 'value' => 'download'],
|
||||
['label' => '每日签到', 'value' => 'signin'],
|
||||
['label' => '前台投稿', 'value' => 'front_submit'],
|
||||
|
||||
@@ -115,30 +115,29 @@ class SchemaUsers {
|
||||
'groups' => [
|
||||
['key' => 'register', 'title' => '注册设置', 'fields' => [
|
||||
['key' => 'close_sign', 'label' => '一键禁用登录注册', 'type' => 'switch', 'default' => false, 'desc' => '开启后前台禁用全部注册/登录入口(含社交登录),不影响后台登录'],
|
||||
['key' => 'user_close_signup', 'label' => '关闭注册', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'close_signin', 'label' => '禁用账密登录', 'type' => 'switch', 'default' => false, 'desc' => '仅允许社交账号登录'],
|
||||
['key' => 'user_agreement_s', 'label' => '用户协议', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'close_signup', 'label' => '关闭注册', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'close_signin', 'label' => '禁用账号登录/注册', 'type' => 'switch', 'default' => false, 'desc' => '关闭账号密码和验证码登录注册,仅允许社交账号登录'],
|
||||
['key' => 'user_agreement_s', 'label' => '用户协议', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'user_agreement_page', 'label' => '协议页面', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'user_privacy_s', 'label' => '隐私政策', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_privacy_s', 'label' => '隐私政策', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'user_privacy_page', 'label' => '隐私页面', 'type' => 'input', 'default' => ''],
|
||||
['key' => 'user_agreement_check', 'label' => '强制勾选协议', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'no_repetition_name', 'label' => '禁止重复昵称', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'user_nickname_out', 'label' => '昵称黑名单关键词', 'type' => 'textarea', 'default' => "赌博,博彩,代发,代理,刷单,博彩,六合彩,admin,test,管理员,客服", 'desc' => '一行一个关键词,注册昵称包含关键词将被拒绝'],
|
||||
['key' => 'user_email_limit_type', 'label' => '邮箱限制类型', 'type' => 'radio', 'default' => 'black', 'options' => [
|
||||
['label' => '黑名单(拒绝以下后缀)', 'value' => 'black'],
|
||||
['label' => '白名单(仅允许以下后缀)', 'value' => 'white'],
|
||||
['key' => 'user_agreement_check', 'label' => '强制勾选协议', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'no_repetition_name', 'label' => '禁止重复昵称', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'user_nickname_out', 'label' => '昵称黑名单关键词', 'type' => 'textarea', 'default' => '赌博,博彩,彩票,性爱,色情,做爱,爱爱,淫秽,傻b,妈的,妈b,admin,test'],
|
||||
['key' => 'user_email_limit_type', 'label' => '邮箱限制类型', 'type' => 'radio', 'default' => 'not', 'options' => [
|
||||
['label' => '黑名单', 'value' => 'not'],
|
||||
['label' => '白名单', 'value' => 'allow'],
|
||||
]],
|
||||
['key' => 'user_email_out', 'label' => '邮箱限制后缀', 'type' => 'textarea', 'default' => "@tempmail.com\n@throwaway.email\n@guerrillamail.com", 'desc' => '一行一个邮箱后缀,如 @tempmail.com'],
|
||||
['key' => 'user_email_out', 'label' => '邮箱限制关键词', 'type' => 'textarea', 'default' => ''],
|
||||
]],
|
||||
['key' => 'login', 'title' => '登录设置', 'fields' => [
|
||||
['key' => 'user_close_login', 'label' => '关闭登录', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'user_sign_type', 'label' => '登录注册样式', 'type' => 'radio', 'default' => 'page', 'options' => [
|
||||
['key' => 'user_sign_type', 'label' => '登录注册样式', 'type' => 'radio', 'default' => 'modal', 'options' => [
|
||||
['label' => '弹窗', 'value' => 'modal'],
|
||||
['label' => '跳转页面', 'value' => 'page'],
|
||||
]],
|
||||
['key' => 'sign_modal_auto_s', 'label' => '自动弹出登录窗', 'type' => 'switch', 'default' => false, 'desc' => '仅在 user_sign_type=modal 时生效'],
|
||||
['key' => 'sign_modal_auto_waiting_time', 'label' => '弹窗等待秒数', 'type' => 'number', 'default' => 5, 'min' => 0, 'max' => 60, 'desc' => '页面加载多少秒后弹出登录窗'],
|
||||
['key' => 'sign_modal_auto_expires', 'label' => '弹窗周期(小时)', 'type' => 'number', 'default' => 1, 'min' => 0, 'max' => 720, 'desc' => '关闭弹窗后多少小时内不再自动弹出,0=每次刷新都弹'],
|
||||
['key' => 'sign_modal_auto_waiting_time', 'label' => '弹窗等待秒数', 'type' => 'number', 'default' => 5, 'min' => 1, 'max' => 100, 'desc' => '页面加载多少秒后弹出登录窗'],
|
||||
['key' => 'sign_modal_auto_expires', 'label' => '弹窗周期(小时)', 'type' => 'number', 'default' => 1, 'min' => 0, 'max' => 2000, 'desc' => '关闭弹窗后多少小时内不再自动弹出,0=每次刷新都弹'],
|
||||
['key' => 'replace_wp_login', 'label' => '替换WP登录页', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'admin_sign_key', 'label' => '应急登录 Key', 'type' => 'input', 'default' => '', 'desc' => '留空为禁用。管理员可通过 /wp-login.php?aksrt_bypass=<key> 绕过前台替换'],
|
||||
['key' => 'user_signin_phone_s', 'label' => '手机号登录', 'type' => 'switch', 'default' => false, 'desc' => '允许用手机号作为用户名登录'],
|
||||
@@ -148,20 +147,20 @@ class SchemaUsers {
|
||||
['label' => '手机验证码', 'value' => 'phone'],
|
||||
['label' => '邮箱或手机', 'value' => 'email_phone'],
|
||||
]],
|
||||
['key' => 'user_signin_nopas_active', 'label' => '免密显示模式', 'type' => 'radio', 'default' => 'nopas_first', 'options' => [
|
||||
['label' => '优先免密', 'value' => 'nopas_first'],
|
||||
['label' => '优先密码', 'value' => 'password_first'],
|
||||
['label' => '仅免密', 'value' => 'nopas_only'],
|
||||
['key' => 'user_signin_nopas_active', 'label' => '免密显示模式', 'type' => 'radio', 'default' => 'nopas', 'options' => [
|
||||
['label' => '优先免密', 'value' => 'nopas'],
|
||||
['label' => '优先密码', 'value' => 'pas'],
|
||||
['label' => '仅免密', 'value' => 'only_nopas'],
|
||||
]],
|
||||
]],
|
||||
['key' => 'signup_captcha', 'title' => '注册验证', 'fields' => [
|
||||
['key' => 'user_signup_captch', 'label' => '注册需验证', 'type' => 'switch', 'default' => true, 'desc' => '注册时需完成邮箱或手机验证'],
|
||||
['key' => 'user_signup_captch', 'label' => '注册需验证', 'type' => 'switch', 'default' => false, 'desc' => '验证码通过后才创建并登录用户'],
|
||||
['key' => 'captch_type', 'label' => '验证方式', 'type' => 'radio', 'default' => 'email', 'options' => [
|
||||
['label' => '邮箱', 'value' => 'email'],
|
||||
['label' => '手机', 'value' => 'phone'],
|
||||
['label' => '邮箱或手机', 'value' => 'email_phone'],
|
||||
]],
|
||||
['key' => 'user_signup_no_repas', 'label' => '无需重复密码', 'type' => 'switch', 'default' => true, 'desc' => '注册时不要求二次确认密码'],
|
||||
['key' => 'user_signup_no_repas', 'label' => '无需重复密码', 'type' => 'switch', 'default' => true, 'desc' => '注册时不要求二次确认密码', 'dependsOn' => ['key' => 'user_signup_captch', 'value' => true]],
|
||||
['key' => 'user_repas_captch_type', 'label' => '找回密码验证方式', 'type' => 'radio', 'default' => 'email', 'options' => [
|
||||
['label' => '邮箱', 'value' => 'email'],
|
||||
['label' => '手机', 'value' => 'phone'],
|
||||
@@ -176,8 +175,8 @@ class SchemaUsers {
|
||||
['label' => '提醒绑定邮箱和手机', 'value' => 'email_phone'],
|
||||
['label' => '关闭', 'value' => 'close'],
|
||||
]],
|
||||
['key' => 'bind_reminder_text', 'label' => '提醒文案', 'type' => 'input', 'default' => '请绑定手机号以正常使用'],
|
||||
['key' => 'bind_reminder_expires', 'label' => '提醒周期(小时)', 'type' => 'number', 'default' => 24, 'min' => 1, 'max' => 720, 'desc' => '用户关闭提醒后多少小时内不再提醒'],
|
||||
['key' => 'bind_reminder_text', 'label' => '提醒文案', 'type' => 'textarea', 'default' => '为了您的账户安全,请务必完成账户绑定'],
|
||||
['key' => 'bind_reminder_expires', 'label' => '提醒周期(小时)', 'type' => 'number', 'default' => 24, 'min' => 0, 'max' => 2000],
|
||||
['key' => 'mandatory_bind', 'label' => '强制绑定', 'type' => 'radio', 'default' => 'close', 'options' => [
|
||||
['label' => '强制绑定邮箱', 'value' => 'email'],
|
||||
['label' => '强制绑定手机', 'value' => 'phone'],
|
||||
@@ -187,37 +186,34 @@ class SchemaUsers {
|
||||
['key' => 'email_set_captch', 'label' => '绑定邮箱需验证', 'type' => 'switch', 'default' => true, 'desc' => '绑定邮箱时需发送验证码确认'],
|
||||
]],
|
||||
['key' => 'referral_inline', 'title' => '推广码注册', 'fields' => [
|
||||
['key' => 'referral_registration_enabled', 'label' => '开放推广码注册', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'referral_register_field', 'label' => '注册页推广码字段', 'type' => 'select', 'default' => 'optional', 'options' => [
|
||||
['label' => '隐藏', 'value' => 'hidden'],
|
||||
['label' => '选填', 'value' => 'optional'],
|
||||
['label' => '必填', 'value' => 'required'],
|
||||
['key' => 'invit_code_s', 'label' => '推广码注册模式', 'type' => 'radio', 'default' => 'close', 'options' => [
|
||||
['label' => '关闭', 'value' => 'close'],
|
||||
['label' => '选填', 'value' => 'open'],
|
||||
['label' => '必填', 'value' => 'must'],
|
||||
]],
|
||||
['key' => 'referral_reject_invalid', 'label' => '拒绝无效推广码', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'referral_code_length', 'label' => 'Ref 码长度', 'type' => 'number', 'default' => 12, 'min' => 4, 'max' => 12],
|
||||
['key' => 'referral_window_days', 'label' => '首次触达窗口(天)', 'type' => 'number', 'default' => 30, 'min' => 1, 'max' => 365],
|
||||
['key' => 'referral_open_desc', 'label' => '选填提示', 'type' => 'input', 'default' => '推广码为选填,没有推广码也可以注册'],
|
||||
['key' => 'referral_required_desc', 'label' => '必填提示', 'type' => 'input', 'default' => '请输入推广码'],
|
||||
['key' => 'mandatory_bind_text', 'label' => '强制绑定文案', 'type' => 'textarea', 'default' => '为了您的账户安全,请先完成账户绑定'],
|
||||
['key' => 'invit_code_open_desc', 'label' => '选填提示', 'type' => 'input', 'default' => '如您受到邀请,可填写推广码。部分推广码会有额外积分赠送'],
|
||||
['key' => 'invit_code_must_desc', 'label' => '必填提示', 'type' => 'textarea', 'default' => '<div>本站需使用推广码注册</div><div>获取推广码请与客服联系</div>'],
|
||||
]],
|
||||
['key' => 'sign_page_ui', 'title' => '页面 UI 配置', 'fields' => [
|
||||
['key' => 'sign_page_background', 'label' => '页面背景图', 'type' => 'gallery', 'default' => [], 'desc' => '登录注册页背景图,多图随机'],
|
||||
['key' => 'sign_theme_mode', 'label' => '主题模式', 'type' => 'radio', 'default' => 'default', 'options' => [
|
||||
['label' => '默认', 'value' => 'default'],
|
||||
['label' => '亮色', 'value' => 'light'],
|
||||
['label' => '暗色', 'value' => 'dark'],
|
||||
['key' => 'sign_theme_mode', 'label' => '主题模式', 'type' => 'radio', 'default' => 'no', 'options' => [
|
||||
['label' => '默认', 'value' => 'no'],
|
||||
['label' => '亮色', 'value' => 'white-theme'],
|
||||
['label' => '暗色', 'value' => 'dark-theme'],
|
||||
]],
|
||||
['key' => 'sign_show_header', 'label' => '显示顶部导航', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'sign_card_position', 'label' => '卡片位置', 'type' => 'radio', 'default' => 'center', 'options' => [
|
||||
['key' => 'sign_card_position', 'label' => '卡片位置', 'type' => 'radio', 'default' => 'right', 'options' => [
|
||||
['label' => '靠左', 'value' => 'left'],
|
||||
['label' => '居中', 'value' => 'center'],
|
||||
['label' => '靠右', 'value' => 'right'],
|
||||
]],
|
||||
['key' => 'sign_show_logo', 'label' => '显示 LOGO', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'sign_show_logo', 'label' => '显示 LOGO', 'type' => 'switch', 'default' => false],
|
||||
['key' => 'sign_page_footer', 'label' => '页脚 HTML', 'type' => 'textarea', 'default' => '', 'desc' => '登录注册页自定义页脚内容,支持 HTML'],
|
||||
]],
|
||||
['key' => 'modal_ui', 'title' => '弹窗 UI 配置', 'fields' => [
|
||||
['key' => 'sign_modal_background', 'label' => '弹窗背景图', 'type' => 'gallery', 'default' => [], 'desc' => '弹窗左侧背景图,多图随机'],
|
||||
['key' => 'sign_modal_show_logo', 'label' => '弹窗显示 LOGO', 'type' => 'switch', 'default' => true],
|
||||
['key' => 'sign_modal_show_logo', 'label' => '弹窗显示 LOGO', 'type' => 'switch', 'default' => false],
|
||||
]],
|
||||
['key' => 'sign_logo', 'title' => '登录框 LOGO', 'fields' => [
|
||||
['key' => 'user_logo', 'label' => '日间 LOGO', 'type' => 'gallery', 'default' => [], 'desc' => '登录框专用日间 LOGO,留空使用站点全局 LOGO'],
|
||||
|
||||
@@ -17,20 +17,6 @@ class ToolsController extends ControllerBase {
|
||||
}
|
||||
|
||||
public function register_routes(): void {
|
||||
// Health: email verification status
|
||||
register_rest_route(self::API_NAMESPACE, '/health/email-verification', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'rest_health_email_verification'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// Health: REST block toggle
|
||||
register_rest_route(self::API_NAMESPACE, '/health/rest-block', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'rest_set_rest_block'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// Email rollback: list users with recent email changes
|
||||
register_rest_route(self::API_NAMESPACE, '/email-rollback/users', [
|
||||
'methods' => 'GET',
|
||||
@@ -213,35 +199,6 @@ class ToolsController extends ControllerBase {
|
||||
]);
|
||||
}
|
||||
|
||||
public function rest_health_email_verification(): \WP_REST_Response {
|
||||
$feature_enabled = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::registration_requires_email_verification();
|
||||
$pending_users = 0;
|
||||
|
||||
if (class_exists('\AKSRT\Auth\EmailVerification')) {
|
||||
global $wpdb;
|
||||
$pending_users = (int) $wpdb->get_var(
|
||||
"SELECT COUNT(*) FROM {$wpdb->usermeta} WHERE meta_key = 'aksrt_email_verified' AND CAST(meta_value AS UNSIGNED) = 0"
|
||||
);
|
||||
}
|
||||
|
||||
return $this->success(['data' => [
|
||||
'feature_enabled' => $feature_enabled,
|
||||
'pending_users' => $pending_users,
|
||||
'rest_blocking_active' => !empty(get_option('aksrt_rest_block_email_verification', false)),
|
||||
]]);
|
||||
}
|
||||
|
||||
public function rest_set_rest_block(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$enabled = !empty($params['enabled']);
|
||||
update_option('aksrt_rest_block_email_verification', $enabled);
|
||||
|
||||
return $this->success([
|
||||
'message' => $enabled ? __('已启用 REST 邮箱验证阻断', 'aksrt') : __('已关闭 REST 邮箱验证阻断', 'aksrt'),
|
||||
'data' => ['enabled' => $enabled],
|
||||
]);
|
||||
}
|
||||
|
||||
public function rest_email_rollback_list(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Auth\EmailRollback')) {
|
||||
return $this->success(['data' => ['items' => [], 'total' => 0, 'page' => 1, 'per_page' => 20]]);
|
||||
|
||||
@@ -207,27 +207,36 @@ class UserController extends ControllerBase {
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// ── Modal Auth (弹窗登录/注册) ──
|
||||
|
||||
// Modal login
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/modal-login', [
|
||||
// Shared public authentication contract for modal and /sign/.
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/code', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'modal_login'],
|
||||
'callback' => [$this, 'auth_code'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// Modal register
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/modal-register', [
|
||||
register_rest_route(self::API_NAMESPACE, '/user/phone-code', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'user_phone_code'],
|
||||
'permission_callback' => static function () { return is_user_logged_in(); },
|
||||
]);
|
||||
register_rest_route(self::API_NAMESPACE, '/user/phone', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'user_bind_phone'],
|
||||
'permission_callback' => static function () { return is_user_logged_in(); },
|
||||
]);
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/login', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'modal_register'],
|
||||
'callback' => [$this, 'auth_login'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// Current user info (for AJAX refresh)
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/me', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_current_user_info'],
|
||||
'permission_callback' => function () { return is_user_logged_in(); },
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/register', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_register'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/reset-password', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_reset_password'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// Identity verification provider settings (admin only)
|
||||
@@ -277,41 +286,6 @@ class UserController extends ControllerBase {
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// SMS auth: send verification code (public, rate-limited)
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/send-sms-code', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_send_sms_code'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// SMS auth: register by phone (public)
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/phone-register', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_phone_register'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// SMS auth: login by phone + password (public)
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/phone-login', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_phone_login'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// SMS auth: login by phone + SMS code (public)
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/phone-code-login', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_phone_code_login'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
// SMS auth: bind phone to current user (logged-in)
|
||||
register_rest_route(self::API_NAMESPACE, '/auth/bind-phone', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'auth_bind_phone'],
|
||||
'permission_callback' => function () { return is_user_logged_in(); },
|
||||
]);
|
||||
|
||||
// ── User Restrictions / Ban ──
|
||||
|
||||
// List restrictions for a user (admin)
|
||||
@@ -401,22 +375,7 @@ class UserController extends ControllerBase {
|
||||
|
||||
if ($request->get_method() === 'GET') {
|
||||
$data = \AKSRT\Settings\Settings::get_all();
|
||||
$referral = class_exists('\AKSRT\Settings\CreditsRuntime')
|
||||
? \AKSRT\Settings\CreditsRuntime::get_referral_registration_settings()
|
||||
: [];
|
||||
$shared = class_exists('\AKSRT\Settings\CreditsRuntime')
|
||||
? \AKSRT\Settings\CreditsRuntime::get_referral_settings()
|
||||
: [];
|
||||
|
||||
return $this->success(['data' => [
|
||||
'registration_email_verification_enabled' => \AKSRT\Settings\Settings::registration_requires_email_verification(),
|
||||
'registration_email_verification_method' => \AKSRT\Settings\Settings::registration_verification_method(),
|
||||
'referral_registration_enabled' => $referral['enabled'] ?? true,
|
||||
'referral_code_length' => $shared['code_length'] ?? 12,
|
||||
'referral_window_days' => $shared['window_days'] ?? 30,
|
||||
'referral_register_field' => $referral['register_field'] ?? 'optional',
|
||||
'referral_reject_invalid' => $referral['reject_invalid'] ?? true,
|
||||
] + array_intersect_key($data, $allowed)]);
|
||||
return $this->success(['data' => array_intersect_key($data, $allowed)]);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
@@ -424,30 +383,6 @@ class UserController extends ControllerBase {
|
||||
$sanitized = $this->sanitize_fields($params, $allowed);
|
||||
$settings = array_merge($settings, $sanitized);
|
||||
|
||||
if (array_key_exists('referral_registration_enabled', $params)) {
|
||||
$settings['referral_registration_enabled'] = !empty($params['referral_registration_enabled']);
|
||||
}
|
||||
if (array_key_exists('referral_code_length', $params)) {
|
||||
$settings['referral_code_length'] = max(4, min(12, (int) $params['referral_code_length']));
|
||||
}
|
||||
if (array_key_exists('referral_window_days', $params)) {
|
||||
$settings['referral_window_days'] = max(1, min(365, (int) $params['referral_window_days']));
|
||||
}
|
||||
if (array_key_exists('referral_register_field', $params)) {
|
||||
$field = sanitize_key((string) $params['referral_register_field']);
|
||||
$settings['referral_register_field'] = in_array($field, ['hidden', 'optional', 'required'], true) ? $field : 'optional';
|
||||
}
|
||||
if (array_key_exists('referral_reject_invalid', $params)) {
|
||||
$settings['referral_reject_invalid'] = !empty($params['referral_reject_invalid']);
|
||||
}
|
||||
if (array_key_exists('registration_email_verification_enabled', $params)) {
|
||||
$settings['registration_email_verification_enabled'] = !empty($params['registration_email_verification_enabled']);
|
||||
}
|
||||
if (array_key_exists('registration_email_verification_method', $params)) {
|
||||
$method = (string) $params['registration_email_verification_method'];
|
||||
$settings['registration_email_verification_method'] = in_array($method, ['link', 'code'], true) ? $method : 'link';
|
||||
}
|
||||
|
||||
update_option('aksrt_settings', $settings);
|
||||
\AKSRT\Settings\Settings::flush_settings_cache();
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Shared public authentication REST operations.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
@@ -9,286 +11,385 @@ defined('ABSPATH') || exit;
|
||||
|
||||
trait UserControllerAuthPhoneOperations {
|
||||
|
||||
/**
|
||||
* POST /auth/modal-login — 弹窗登录
|
||||
*/
|
||||
public function modal_login(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$payload = is_array($params) ? $params : [];
|
||||
$login = sanitize_text_field((string) ($payload['log'] ?? $payload['login'] ?? ''));
|
||||
$password = (string) ($payload['pwd'] ?? $payload['password'] ?? '');
|
||||
private function auth_payload(\WP_REST_Request $request): array {
|
||||
$params = $this->get_json_params($request);
|
||||
return is_array($params) ? $params : [];
|
||||
}
|
||||
|
||||
if ($login === '' || $password === '') {
|
||||
return $this->error('请输入用户名和密码');
|
||||
}
|
||||
private function auth_verify_rest_nonce(\WP_REST_Request $request, array $payload): bool {
|
||||
$nonce = sanitize_text_field((string) ($request->get_header('X-WP-Nonce') ?: ($payload['_wpnonce'] ?? '')));
|
||||
return $nonce !== '' && (bool) wp_verify_nonce($nonce, 'wp_rest');
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error('认证服务不可用', 500);
|
||||
}
|
||||
private function auth_rate_key(string $action, string $subject = ''): string {
|
||||
$ip = sanitize_text_field((string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'));
|
||||
return 'aksrt_auth_rate_' . md5($action . '|' . strtolower($subject) . '|' . $ip);
|
||||
}
|
||||
|
||||
$result = \AKSRT\Auth\Service::login_user([
|
||||
...$payload,
|
||||
'log' => $login,
|
||||
'pwd' => $password,
|
||||
'rememberme' => !empty($payload['rememberme']) ? (string) $payload['rememberme'] : 'forever',
|
||||
], true, true);
|
||||
private function auth_rate_limited(string $action, string $subject = '', int $limit = 8, int $ttl = 600): bool {
|
||||
$key = $this->auth_rate_key($action, $subject);
|
||||
$count = (int) get_transient($key);
|
||||
if ($count >= $limit) {
|
||||
return true;
|
||||
}
|
||||
set_transient($key, $count + 1, $ttl);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($result['success'])) {
|
||||
$error = $result['error'] ?? '登录失败';
|
||||
$verify_url = $result['verify_url'] ?? '';
|
||||
$response = ['success' => false, 'message' => $error];
|
||||
if ($verify_url) {
|
||||
$response['verify_url'] = $verify_url;
|
||||
}
|
||||
return new \WP_REST_Response($response, 401);
|
||||
}
|
||||
private function auth_code_key(string $scene, string $target): string {
|
||||
return 'aksrt_auth_code_' . md5($scene . '|' . strtolower(trim($target)));
|
||||
}
|
||||
|
||||
$user = wp_get_current_user();
|
||||
return $this->success([
|
||||
'message' => __('登录成功', 'aksrt'),
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
'user' => [
|
||||
'id' => $user->ID,
|
||||
'display_name' => $user->display_name,
|
||||
'avatar_url' => function_exists('aksrt_get_avatar_url')
|
||||
? aksrt_get_avatar_url($user->ID, ['size' => 48])
|
||||
: get_avatar_url($user->ID, ['size' => 48]),
|
||||
],
|
||||
]);
|
||||
}
|
||||
private function auth_store_email_code(string $scene, string $target, string $code): void {
|
||||
set_transient($this->auth_code_key($scene, $target), [
|
||||
'hash' => wp_hash_password($code),
|
||||
'attempts' => 0,
|
||||
], 10 * MINUTE_IN_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /auth/modal-register — 弹窗注册
|
||||
*/
|
||||
public function modal_register(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$payload = is_array($params) ? $params : [];
|
||||
$username = sanitize_user((string) ($payload['user_login'] ?? $payload['username'] ?? ''), true);
|
||||
$email = sanitize_email((string) ($payload['user_email'] ?? $payload['email'] ?? ''));
|
||||
$password = (string) ($payload['user_pass'] ?? $payload['password'] ?? '');
|
||||
$confirm = (string) ($payload['user_pass_confirm'] ?? $payload['password_confirm'] ?? $payload['confirm_password'] ?? $password);
|
||||
$ref = sanitize_key((string) ($payload['ref'] ?? ''));
|
||||
$register_nonce = sanitize_text_field((string) ($payload['_register_nonce'] ?? ''));
|
||||
$agree_terms = !empty($payload['agree_terms']) ? '1' : '';
|
||||
private function auth_consume_code(string $scene, string $target, string $code, string $channel): bool {
|
||||
if ($channel === 'phone') {
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) {
|
||||
return false;
|
||||
}
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->verify_code($target, $code);
|
||||
return !empty($result['success']);
|
||||
}
|
||||
|
||||
if ($username === '' || $email === '' || $password === '') {
|
||||
return $this->error('请填写所有必填项');
|
||||
}
|
||||
$key = $this->auth_code_key($scene, $target);
|
||||
$stored = get_transient($key);
|
||||
if (!is_array($stored) || empty($stored['hash'])) {
|
||||
return false;
|
||||
}
|
||||
$attempts = (int) ($stored['attempts'] ?? 0) + 1;
|
||||
if ($attempts > 6 || !wp_check_password($code, (string) $stored['hash'])) {
|
||||
$stored['attempts'] = $attempts;
|
||||
set_transient($key, $stored, 10 * MINUTE_IN_SECONDS);
|
||||
return false;
|
||||
}
|
||||
delete_transient($key);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_email($email)) {
|
||||
return $this->error('邮箱格式不正确');
|
||||
}
|
||||
private function auth_channel(string $target, string $requested = ''): string {
|
||||
if ($requested === 'phone' || preg_match('/^1[3-9]\d{9}$/', $target)) {
|
||||
return 'phone';
|
||||
}
|
||||
return 'email';
|
||||
}
|
||||
|
||||
if (strlen($password) < 6) {
|
||||
return $this->error('密码至少 6 位');
|
||||
}
|
||||
private function auth_channel_allowed(string $configured, string $channel): bool {
|
||||
return $configured === 'email_phone' || $configured === $channel;
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error('认证服务不可用', 500);
|
||||
}
|
||||
private function auth_safe_redirect(string $requested, string $fallback = ''): string {
|
||||
$fallback = $fallback !== '' ? $fallback : home_url('/');
|
||||
return wp_validate_redirect($requested, $fallback);
|
||||
}
|
||||
|
||||
$payload['user_login'] = $username;
|
||||
$payload['user_email'] = $email;
|
||||
$payload['user_pass'] = $password;
|
||||
$payload['user_pass_confirm'] = $confirm;
|
||||
$payload['ref'] = $ref;
|
||||
$payload['_register_nonce'] = $register_nonce;
|
||||
$payload['agree_terms'] = $agree_terms;
|
||||
private function auth_registration_error(string $error): string {
|
||||
if (str_contains($error, '已被') || str_contains($error, '已注册') || str_contains($error, '已使用')) {
|
||||
return __('注册信息不可用,请检查后重试', 'aksrt');
|
||||
}
|
||||
return $error;
|
||||
}
|
||||
|
||||
$result = \AKSRT\Auth\Service::register_user($payload);
|
||||
private function auth_find_user(string $target, string $channel) {
|
||||
if ($channel === 'phone') {
|
||||
$users = get_users([
|
||||
'meta_key' => 'aksrt_phone',
|
||||
'meta_value' => $target,
|
||||
'number' => 1,
|
||||
]);
|
||||
return $users[0] ?? null;
|
||||
}
|
||||
return is_email($target)
|
||||
? get_user_by('email', sanitize_email($target))
|
||||
: get_user_by('login', sanitize_user($target, true));
|
||||
}
|
||||
|
||||
if (empty($result['success'])) {
|
||||
return $this->error($result['error'] ?? '注册失败');
|
||||
}
|
||||
public function auth_code(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
}
|
||||
|
||||
$verification_required = !empty($result['verification_required']);
|
||||
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url();
|
||||
$registered_user = null;
|
||||
$current_user = wp_get_current_user();
|
||||
if (
|
||||
$current_user instanceof \WP_User
|
||||
&& $current_user->ID > 0
|
||||
&& !empty($result['user_id'])
|
||||
&& (int) $result['user_id'] === (int) $current_user->ID
|
||||
) {
|
||||
$registered_user = $current_user;
|
||||
}
|
||||
$user = $current_user;
|
||||
$needs_verify = $verification_required;
|
||||
$scene = sanitize_key((string) ($payload['scene'] ?? ''));
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? '')));
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
||||
$oauth_bind = $obk !== '' && class_exists('\AKSRT\Auth\Service') && is_array(\AKSRT\Auth\Service::get_oauth_bind_context($obk));
|
||||
if (!in_array($scene, ['login', 'register', 'resetpassword'], true)) {
|
||||
return $this->error(__('请求类型无效', 'aksrt'));
|
||||
}
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
if (\AKSRT\Settings\Settings::close_sign()) return $this->error(__('登录注册已关闭', 'aksrt'), 403);
|
||||
if (($scene === 'login' || $scene === 'resetpassword') && \AKSRT\Settings\Settings::close_signin() && !$oauth_bind) return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
||||
if ($scene === 'register' && !\AKSRT\Settings\Settings::registration_enabled() && !$oauth_bind) return $this->error(__('当前已关闭新用户注册', 'aksrt'), 403);
|
||||
$type = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_type() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_type() : \AKSRT\Settings\Settings::reset_password_verification_type());
|
||||
$enabled = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_enabled() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_enabled() : true);
|
||||
if (!$enabled || ($type !== 'email_phone' && $type !== $channel)) return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
}
|
||||
if ($this->auth_rate_limited('code:' . $scene, $target, 5, HOUR_IN_SECONDS)) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
}
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_scene = $scene === 'resetpassword' ? 'lostpassword' : $scene;
|
||||
$captcha = aksrt_require_captcha($captcha_scene, $payload);
|
||||
if ($captcha !== true) {
|
||||
return $this->error((string) $captcha);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'message' => $needs_verify ? __('注册成功,请验证邮箱', 'aksrt') : __('注册成功', 'aksrt'),
|
||||
'needs_verification' => $needs_verify,
|
||||
'verify_url' => $result['verify_url'] ?? '',
|
||||
'redirect' => $verification_required ? ($result['verify_url'] ?? '') : $login_url,
|
||||
'verification_required' => $verification_required,
|
||||
'login_url' => $login_url,
|
||||
'user_login' => $result['user_login'] ?? $username,
|
||||
'user_email' => $email,
|
||||
'notice' => (string) ($result['notice'] ?? ''),
|
||||
'user' => $registered_user ? [
|
||||
'id' => $registered_user->ID,
|
||||
'display_name' => $registered_user->display_name,
|
||||
'avatar_url' => function_exists('aksrt_get_avatar_url')
|
||||
? aksrt_get_avatar_url($registered_user->ID, ['size' => 48])
|
||||
: get_avatar_url($registered_user->ID, ['size' => 48]),
|
||||
] : ($user->ID ? [
|
||||
'id' => $user->ID,
|
||||
'display_name' => $user->display_name,
|
||||
'avatar_url' => function_exists('aksrt_get_avatar_url')
|
||||
? aksrt_get_avatar_url($user->ID, ['size' => 48])
|
||||
: get_avatar_url($user->ID, ['size' => 48]),
|
||||
] : null),
|
||||
]);
|
||||
}
|
||||
$generic = __('如果信息有效,验证码将发送到该账号', 'aksrt');
|
||||
if ($scene !== 'register' && !$this->auth_find_user($target, $channel)) {
|
||||
return $this->success(['message' => $generic, 'countdown' => 60]);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /auth/me — 当前用户信息(用于 AJAX 刷新)
|
||||
*/
|
||||
public function get_current_user_info(): \WP_REST_Response {
|
||||
$user = wp_get_current_user();
|
||||
if (!$user->ID) {
|
||||
return $this->error('未登录', 401);
|
||||
}
|
||||
if ($channel === 'phone') {
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) {
|
||||
return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
}
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($target);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error((string) ($result['message'] ?? __('验证码发送失败', 'aksrt')));
|
||||
}
|
||||
return $this->success(['message' => $generic, 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
||||
}
|
||||
|
||||
$credits = 0;
|
||||
if (class_exists('\AKSRT\Credits\Credits')) {
|
||||
$credits = \AKSRT\Credits\Credits::balance($user->ID);
|
||||
}
|
||||
$code = (string) random_int(100000, 999999);
|
||||
$this->auth_store_email_code($scene, $target, $code);
|
||||
$subject = sprintf(__('[%s] 登录注册验证码', 'aksrt'), get_bloginfo('name'));
|
||||
$message = sprintf(__('您的验证码是:%s,10 分钟内有效。', 'aksrt'), $code);
|
||||
if (!wp_mail($target, $subject, $message)) {
|
||||
delete_transient($this->auth_code_key($scene, $target));
|
||||
if ($scene !== 'register') {
|
||||
return $this->success(['message' => $generic, 'countdown' => 60]);
|
||||
}
|
||||
return $this->error(__('验证码发送失败,请稍后再试', 'aksrt'), 500);
|
||||
}
|
||||
return $this->success(['message' => $generic, 'countdown' => 60]);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'data' => [
|
||||
'id' => $user->ID,
|
||||
'display_name' => $user->display_name,
|
||||
'user_email' => $user->user_email,
|
||||
'avatar_url' => function_exists('aksrt_get_avatar_url')
|
||||
? aksrt_get_avatar_url($user->ID, ['size' => 48])
|
||||
: get_avatar_url($user->ID, ['size' => 48]),
|
||||
'is_admin' => current_user_can('manage_options'),
|
||||
'credits' => $credits,
|
||||
],
|
||||
]);
|
||||
}
|
||||
public function auth_login(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
}
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? $payload['log'] ?? '')));
|
||||
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
||||
$oauth_data = $obk !== '' && class_exists('\AKSRT\Auth\Service')
|
||||
? \AKSRT\Auth\Service::get_oauth_bind_context($obk)
|
||||
: null;
|
||||
if ($obk !== '' && !$oauth_data) {
|
||||
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'aksrt'), 410);
|
||||
}
|
||||
if (
|
||||
class_exists('\AKSRT\Settings\Settings')
|
||||
&& (\AKSRT\Settings\Settings::close_sign() || (!$oauth_data && \AKSRT\Settings\Settings::close_signin()))
|
||||
) {
|
||||
return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
||||
}
|
||||
if ($target === '' || (preg_match('/^1[3-9]\d{9}$/', $target) && empty($payload['code']) && class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::signin_phone_enabled())) {
|
||||
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
||||
}
|
||||
if ($this->auth_rate_limited('login', $target)) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
}
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha = aksrt_require_captcha($oauth_data ? 'bind' : 'login', $payload);
|
||||
if ($captcha !== true) {
|
||||
return $this->error((string) $captcha);
|
||||
}
|
||||
}
|
||||
$redirect = $this->auth_safe_redirect((string) ($payload['redirect_to'] ?? ''), home_url('/'));
|
||||
|
||||
/**
|
||||
* POST /auth/send-sms-code — 发送短信验证码
|
||||
*/
|
||||
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 (!empty($payload['code'])) {
|
||||
if (!class_exists('\AKSRT\Settings\Settings') || !\AKSRT\Settings\Settings::passwordless_login_enabled()) {
|
||||
return $this->error(__('当前未开启免密登录', 'aksrt'));
|
||||
}
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
$configured = \AKSRT\Settings\Settings::passwordless_login_type();
|
||||
if (!$this->auth_channel_allowed($configured, $channel)) {
|
||||
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
$user = $this->auth_find_user($target, $channel);
|
||||
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']), $channel)) {
|
||||
return $this->error(__('账号或验证码错误', 'aksrt'), 401);
|
||||
}
|
||||
wp_set_current_user((int) $user->ID);
|
||||
wp_set_auth_cookie((int) $user->ID, !empty($payload['rememberme']));
|
||||
do_action('wp_login', $user->user_login, $user);
|
||||
if ($oauth_data) {
|
||||
\AKSRT\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
||||
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $redirect;
|
||||
}
|
||||
return $this->success(['message' => $oauth_data ? __('账号绑定成功', 'aksrt') : __('登录成功', 'aksrt'), 'redirect' => $redirect]);
|
||||
}
|
||||
|
||||
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\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
}
|
||||
$payload['log'] = $target;
|
||||
$payload['pwd'] = (string) ($payload['password'] ?? $payload['pwd'] ?? '');
|
||||
$payload['redirect_to'] = $redirect;
|
||||
$result = \AKSRT\Auth\Service::login_user($payload, $oauth_data ? 'bind' : 'login');
|
||||
if (empty($result['success'])) {
|
||||
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
||||
}
|
||||
if ($oauth_data) {
|
||||
$user_id = get_current_user_id();
|
||||
if ($user_id <= 0) {
|
||||
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
||||
}
|
||||
\AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $redirect;
|
||||
}
|
||||
return $this->success(['message' => $oauth_data ? __('账号绑定成功', 'aksrt') : __('登录成功', 'aksrt'), 'redirect' => $redirect]);
|
||||
}
|
||||
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) {
|
||||
return $this->error('短信服务不可用');
|
||||
}
|
||||
public function auth_register(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
}
|
||||
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
||||
$oauth_data = $obk !== '' && class_exists('\AKSRT\Auth\Service')
|
||||
? \AKSRT\Auth\Service::get_oauth_bind_context($obk)
|
||||
: null;
|
||||
if ($obk !== '' && !$oauth_data) {
|
||||
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'aksrt'), 410);
|
||||
}
|
||||
if (class_exists('\AKSRT\Settings\Settings') && (\AKSRT\Settings\Settings::close_sign() || (!$oauth_data && !\AKSRT\Settings\Settings::registration_enabled()))) {
|
||||
return $this->error(__('当前已关闭新用户注册', 'aksrt'), 403);
|
||||
}
|
||||
if ($this->auth_rate_limited('register')) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
}
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? $payload['user_email'] ?? $payload['phone'] ?? '')));
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
$signup_verification = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::signup_verification_enabled();
|
||||
$configured = $signup_verification ? \AKSRT\Settings\Settings::signup_verification_type() : 'email';
|
||||
if (!$this->auth_channel_allowed($configured, $channel)) {
|
||||
return $this->error(__('当前注册方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
}
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha = aksrt_require_captcha('register', $payload);
|
||||
if ($captcha !== true) {
|
||||
return $this->error((string) $captcha);
|
||||
}
|
||||
}
|
||||
if ($signup_verification) {
|
||||
if (!$this->auth_consume_code('register', $target, sanitize_text_field((string) ($payload['code'] ?? '')), $channel)) {
|
||||
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
||||
}
|
||||
$payload['_auth_code_verified'] = true;
|
||||
}
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
}
|
||||
if ($channel === 'phone') {
|
||||
$payload['phone'] = $target;
|
||||
$result = \AKSRT\Auth\Service::register_by_phone($payload);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'aksrt'))));
|
||||
}
|
||||
$user_id = (int) ($result['user_id'] ?? 0);
|
||||
if ($oauth_data && $user_id > 0) {
|
||||
\AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
}
|
||||
return $this->success(['message' => $oauth_data ? __('注册并绑定成功', 'aksrt') : __('注册成功', 'aksrt'), 'redirect' => $result['redirect'] ?? home_url('/'), 'notice' => (string) ($result['notice'] ?? '')]);
|
||||
}
|
||||
$payload['user_email'] = sanitize_email($target);
|
||||
$payload['user_pass'] = (string) ($payload['password'] ?? '');
|
||||
$payload['user_pass_confirm'] = (string) ($payload['password_confirm'] ?? $payload['password'] ?? '');
|
||||
$result = \AKSRT\Auth\Service::register_user($payload);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'aksrt'))));
|
||||
}
|
||||
$user_id = (int) ($result['user_id'] ?? 0);
|
||||
if ($oauth_data && $user_id > 0) {
|
||||
\AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
||||
}
|
||||
return $this->success([
|
||||
'message' => $oauth_data ? __('注册并绑定成功', 'aksrt') : __('注册成功', 'aksrt'),
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
'notice' => (string) ($result['notice'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($phone);
|
||||
public function auth_reset_password(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
||||
}
|
||||
$target = trim(sanitize_text_field((string) ($payload['target'] ?? '')));
|
||||
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
if (\AKSRT\Settings\Settings::close_sign() || \AKSRT\Settings\Settings::close_signin() || !$this->auth_channel_allowed(\AKSRT\Settings\Settings::reset_password_verification_type(), $channel)) {
|
||||
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
||||
}
|
||||
}
|
||||
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
||||
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
||||
}
|
||||
if ($this->auth_rate_limited('reset-password', $target, 8, HOUR_IN_SECONDS)) {
|
||||
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
||||
}
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha = aksrt_require_captcha('lostpassword', $payload);
|
||||
if ($captcha !== true) {
|
||||
return $this->error((string) $captcha);
|
||||
}
|
||||
}
|
||||
$user = $this->auth_find_user($target, $channel);
|
||||
$password = (string) ($payload['password'] ?? '');
|
||||
$confirm = (string) ($payload['password_confirm'] ?? '');
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
}
|
||||
$password_check = \AKSRT\Auth\Service::validate_password($password, $confirm);
|
||||
if ($password_check !== true) {
|
||||
return $this->error((string) $password_check);
|
||||
}
|
||||
if (!$user || !$this->auth_consume_code('resetpassword', $target, sanitize_text_field((string) ($payload['code'] ?? '')), $channel)) {
|
||||
return $this->error(__('账号或验证码错误', 'aksrt'));
|
||||
}
|
||||
wp_set_password($password, (int) $user->ID);
|
||||
return $this->success(['message' => __('密码已重置,请重新登录', 'aksrt'), 'redirect' => function_exists('aksrt_get_sign_page_url') ? aksrt_get_sign_page_url('signin') : wp_login_url()]);
|
||||
}
|
||||
|
||||
if (!empty($result['success'])) {
|
||||
return $this->success([
|
||||
'message' => $result['message'],
|
||||
'remaining_time' => $result['remaining_time'] ?? 60,
|
||||
]);
|
||||
}
|
||||
public function user_phone_code(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
||||
return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
}
|
||||
$phone = sanitize_text_field((string) ($payload['phone'] ?? ''));
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
return $this->error(__('手机号格式不正确', 'aksrt'));
|
||||
}
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha = aksrt_require_captcha('bind_phone', $payload);
|
||||
if ($captcha !== true) return $this->error((string) $captcha);
|
||||
}
|
||||
if (!class_exists('\AKSRT\Sms\SMS')) return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
||||
$result = \AKSRT\Sms\SMS::get_instance()->send_code($phone);
|
||||
return !empty($result['success'])
|
||||
? $this->success(['message' => __('验证码已发送', 'aksrt'), 'countdown' => (int) ($result['remaining_time'] ?? 60)])
|
||||
: $this->error((string) ($result['message'] ?? __('发送失败', 'aksrt')));
|
||||
}
|
||||
|
||||
$response = [
|
||||
'success' => false,
|
||||
'message' => $result['message'],
|
||||
];
|
||||
if (isset($result['remaining_time'])) {
|
||||
$response['data'] = ['remaining_time' => $result['remaining_time']];
|
||||
}
|
||||
return new \WP_REST_Response($response, 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /auth/phone-register — 手机号注册
|
||||
*/
|
||||
public function auth_phone_register(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error('认证服务不可用');
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$result = \AKSRT\Auth\Service::register_by_phone($params);
|
||||
|
||||
if (!empty($result['success'])) {
|
||||
return $this->success([
|
||||
'message' => $result['message'] ?? '注册成功',
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
'user_id' => $result['user_id'] ?? 0,
|
||||
'notice' => (string) ($result['notice'] ?? ''),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->error($result['error'] ?? '注册失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /auth/phone-login — 手机号 + 密码登录
|
||||
*/
|
||||
public function auth_phone_login(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error('认证服务不可用');
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$result = \AKSRT\Auth\Service::login_by_phone_password($params);
|
||||
|
||||
if (!empty($result['success'])) {
|
||||
return $this->success([
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->error($result['error'] ?? '登录失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /auth/phone-code-login — 手机号 + 验证码免密登录
|
||||
*/
|
||||
public function auth_phone_code_login(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error('认证服务不可用');
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$result = \AKSRT\Auth\Service::login_by_phone_code($params);
|
||||
|
||||
if (!empty($result['success'])) {
|
||||
return $this->success([
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->error($result['error'] ?? '登录失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /auth/bind-phone — 绑定手机号
|
||||
*/
|
||||
public function auth_bind_phone(\WP_REST_Request $request): \WP_REST_Response {
|
||||
if (!class_exists('\AKSRT\Auth\Service')) {
|
||||
return $this->error('认证服务不可用');
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$result = \AKSRT\Auth\Service::bind_phone($params);
|
||||
|
||||
if (!empty($result['success'])) {
|
||||
return $this->success(['message' => $result['message']]);
|
||||
}
|
||||
|
||||
return $this->error($result['error'] ?? '绑定失败');
|
||||
}
|
||||
public function user_bind_phone(\WP_REST_Request $request): \WP_REST_Response {
|
||||
$payload = $this->auth_payload($request);
|
||||
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
||||
if (!class_exists('\AKSRT\Auth\Service')) return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
||||
$result = \AKSRT\Auth\Service::bind_phone($payload);
|
||||
return !empty($result['success']) ? $this->success(['message' => $result['message'] ?? __('绑定成功', 'aksrt')]) : $this->error((string) ($result['error'] ?? __('绑定失败', 'aksrt')));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user