refactor(classes): organize inc/classes into domain folders
Move ~94 root class shells and root *-traits into domain directories (core, auth, user, credits, content, payment/gateways, forum, shop, sms, ai, messages, extensions, widgets, settings, admin). Update bootstrap require paths, co-locate traits with owners, fix forum/user relative requires, drop duplicate shop controller requires, and wire home-modules load. Path-only; class names and init order unchanged.
This commit is contained in:
1 parent
2a8fe68048
commit
ce1c13b947
115 files changed
+171
-224
No files matched your search
@@ -0,0 +1,475 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait AKSRT_Auth_Service_Part_1a {
|
||||
|
||||
|
||||
public static function register_user(array $post, array $context = []): array {
|
||||
// 先 reset pending invite context,避免同一进程内残留污染
|
||||
self::$pending_invite_inviter_id = 0;
|
||||
self::$pending_invite_code = '';
|
||||
|
||||
$user_login = sanitize_user(wp_unslash($post['user_login'] ?? ''), true);
|
||||
$user_email = sanitize_email(wp_unslash($post['user_email'] ?? ''));
|
||||
$password = (string) ($post['user_pass'] ?? '');
|
||||
$confirm = (string) ($post['user_pass_confirm'] ?? '');
|
||||
|
||||
if (!wp_verify_nonce($post['_register_nonce'] ?? '', 'aksrt_register')) {
|
||||
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
|
||||
}
|
||||
|
||||
if (empty($post['agree_terms'])) {
|
||||
return ['success' => false, 'error' => __('请先同意服务条款', 'aksrt')];
|
||||
}
|
||||
|
||||
if (empty($user_login) || !validate_username($user_login)) {
|
||||
return ['success' => false, 'error' => __('请输入有效的用户名', 'aksrt')];
|
||||
}
|
||||
|
||||
if (mb_strlen($user_login) < 3 || mb_strlen($user_login) > 60) {
|
||||
return ['success' => false, 'error' => __('用户名长度需在 3-60 个字符之间', 'aksrt')];
|
||||
}
|
||||
|
||||
if (username_exists($user_login)) {
|
||||
return ['success' => false, 'error' => __('该用户名已被使用', 'aksrt')];
|
||||
}
|
||||
|
||||
if (empty($user_email) || !is_email($user_email)) {
|
||||
return ['success' => false, 'error' => __('请输入有效的邮箱地址', 'aksrt')];
|
||||
}
|
||||
|
||||
if (email_exists($user_email)) {
|
||||
return ['success' => false, 'error' => __('该邮箱已被其他账号使用', 'aksrt')];
|
||||
}
|
||||
|
||||
$password_check = self::validate_password($password, $confirm);
|
||||
if ($password_check !== true) {
|
||||
return ['success' => false, 'error' => $password_check];
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_result = aksrt_require_captcha('register', $post);
|
||||
if ($captcha_result !== true) {
|
||||
return ['success' => false, 'error' => $captcha_result];
|
||||
}
|
||||
}
|
||||
|
||||
// ====== 解析邀请码 ======
|
||||
// 优先级:表单 invite_code(用户显式填写) > cookie aksrt_invite
|
||||
$invite_registration_enabled = class_exists('AKSRT_Settings') && AKSRT_Settings::invite_registration_enabled();
|
||||
$invite_mode = $invite_registration_enabled && class_exists('AKSRT_Settings')
|
||||
? AKSRT_Settings::invite_register_field_mode()
|
||||
: 'hidden';
|
||||
$invite_code = isset($post['invite_code']) ? sanitize_key((string) wp_unslash($post['invite_code'])) : '';
|
||||
$resolved_inviter_id = 0;
|
||||
$invite_is_valid = false;
|
||||
|
||||
if (!$invite_registration_enabled) {
|
||||
// 邀请功能关闭,忽略所有邀请码
|
||||
$invite_code = '';
|
||||
} elseif ($invite_code !== '') {
|
||||
if (class_exists('AKSRT_Invite')) {
|
||||
$invite_service = AKSRT_Invite::get_instance();
|
||||
$resolved_inviter_id = $invite_service->resolve_code($invite_code);
|
||||
$invite_is_valid = $invite_service->validate_code($invite_code);
|
||||
}
|
||||
// 必填模式下,邀请码无效时根据后台开关决定是否拒绝
|
||||
$reject_invalid = class_exists('AKSRT_Settings') && AKSRT_Settings::invite_reject_invalid();
|
||||
if ($invite_mode === 'required' && $reject_invalid && !$invite_is_valid) {
|
||||
return ['success' => false, 'error' => __('邀请码无效,请检查后重试', 'aksrt')];
|
||||
}
|
||||
} elseif ($invite_mode === 'required') {
|
||||
return ['success' => false, 'error' => __('请填写邀请码', 'aksrt')];
|
||||
}
|
||||
|
||||
// 把解析到的 inviter_id 暂存到静态字段,user_register hook 内部读取(优先于 cookie)
|
||||
self::$pending_invite_inviter_id = $resolved_inviter_id;
|
||||
self::$pending_invite_code = $invite_code;
|
||||
|
||||
$user_id = wp_create_user($user_login, $password, $user_email);
|
||||
if (is_wp_error($user_id)) {
|
||||
self::$pending_invite_inviter_id = 0;
|
||||
self::$pending_invite_code = '';
|
||||
return ['success' => false, 'error' => $user_id->get_error_message()];
|
||||
}
|
||||
|
||||
if ($invite_code !== '' && $resolved_inviter_id <= 0 && class_exists('AKSRT_Invite')) {
|
||||
AKSRT_Invite::get_instance()->handle_registration_invite((int) $user_id, $invite_code);
|
||||
}
|
||||
|
||||
// wp_create_user 内部已触发 user_register hook,AKSRT_Invite::on_user_register 已读取 pending 字段。
|
||||
// 这里清空,避免污染后续请求。
|
||||
self::$pending_invite_inviter_id = 0;
|
||||
self::$pending_invite_code = '';
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user || is_wp_error($user)) {
|
||||
return ['success' => false, 'error' => __('注册成功,但读取用户信息失败,请联系管理员', 'aksrt')];
|
||||
}
|
||||
|
||||
$oauth_context = isset($context['oauth_bind_data']) && is_array($context['oauth_bind_data']) ? $context['oauth_bind_data'] : [];
|
||||
$oauth_verified = self::oauth_email_is_verified($oauth_context);
|
||||
$requires_verify = AKSRT_Settings::registration_requires_email_verification() && !$oauth_verified;
|
||||
|
||||
if (!$requires_verify) {
|
||||
self::mark_email_verified($user_id);
|
||||
return [
|
||||
'success' => true,
|
||||
'user_id' => (int) $user_id,
|
||||
'user' => $user,
|
||||
'user_login' => $user_login,
|
||||
];
|
||||
}
|
||||
|
||||
$method = AKSRT_Settings::registration_verification_method();
|
||||
$begin = self::begin_email_verification(
|
||||
$user,
|
||||
$method,
|
||||
[
|
||||
'obk' => sanitize_text_field((string) ($context['obk'] ?? '')),
|
||||
]
|
||||
);
|
||||
|
||||
if (empty($begin['success'])) {
|
||||
self::delete_user_account($user_id);
|
||||
return $begin;
|
||||
}
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'user_id' => (int) $user_id,
|
||||
'user' => $user,
|
||||
'user_login' => $user_login,
|
||||
'verification_required' => true,
|
||||
'verification_method' => $method,
|
||||
'verify_url' => self::get_email_verification_url((int) $user_id, ['mode' => $method]),
|
||||
'message' => $begin['message'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
public static function bind_oauth_account(array $post, array $oauth_data): array {
|
||||
if (!wp_verify_nonce($post['_bind_nonce'] ?? '', 'aksrt_bind')) {
|
||||
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_result = aksrt_require_captcha('bind', $post);
|
||||
if ($captcha_result !== true) {
|
||||
return ['success' => false, 'error' => $captcha_result];
|
||||
}
|
||||
}
|
||||
|
||||
$creds = [
|
||||
'user_login' => self::resolve_email_to_username((string) ($post['log'] ?? '')),
|
||||
'user_password' => (string) ($post['pwd'] ?? ''),
|
||||
'remember' => true,
|
||||
];
|
||||
|
||||
$user = wp_signon($creds);
|
||||
if (is_wp_error($user)) {
|
||||
return ['success' => false, 'error' => self::friendly_login_error($user->get_error_message())];
|
||||
}
|
||||
|
||||
if (self::user_requires_email_verification($user)) {
|
||||
wp_logout();
|
||||
return [
|
||||
'success' => false,
|
||||
'error' => __('该账号尚未完成邮箱验证,请先完成验证后再绑定第三方登录。', 'aksrt'),
|
||||
'verify_url' => self::get_email_verification_url((int) $user->ID),
|
||||
];
|
||||
}
|
||||
|
||||
$provider = sanitize_key((string) ($oauth_data['provider'] ?? ''));
|
||||
if ($provider === '') {
|
||||
return ['success' => false, 'error' => __('绑定数据无效,请重新使用第三方登录', 'aksrt')];
|
||||
}
|
||||
|
||||
$uid = (int) $user->ID;
|
||||
update_user_meta($uid, "aksrt_{$provider}_id", $oauth_data['provider_id'] ?? '');
|
||||
|
||||
$bindings = get_user_meta($uid, 'aksrt_oauth_bindings', true) ?: [];
|
||||
if (!is_array($bindings)) {
|
||||
$bindings = [];
|
||||
}
|
||||
if (!in_array($provider, $bindings, true)) {
|
||||
$bindings[] = $provider;
|
||||
}
|
||||
update_user_meta($uid, 'aksrt_oauth_bindings', $bindings);
|
||||
|
||||
if (!empty($oauth_data['user_data']['avatar_url'])) {
|
||||
update_user_meta($uid, 'aksrt_oauth_avatar', esc_url_raw($oauth_data['user_data']['avatar_url']));
|
||||
}
|
||||
|
||||
if (!empty($post['obk'])) {
|
||||
delete_transient(sanitize_text_field((string) $post['obk']));
|
||||
}
|
||||
|
||||
wp_set_auth_cookie($uid, true);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'user' => $user,
|
||||
'provider' => $provider,
|
||||
];
|
||||
}
|
||||
|
||||
public static function finalize_oauth_registration(int $user_id, array $oauth_data, string $obk = ''): void {
|
||||
if ($user_id <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$provider = sanitize_key((string) ($oauth_data['provider'] ?? ''));
|
||||
if ($provider === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
update_user_meta($user_id, "aksrt_{$provider}_id", $oauth_data['provider_id'] ?? '');
|
||||
update_user_meta($user_id, 'aksrt_oauth_bindings', [$provider]);
|
||||
|
||||
if (!empty($oauth_data['user_data']['avatar_url'])) {
|
||||
update_user_meta($user_id, 'aksrt_oauth_avatar', esc_url_raw($oauth_data['user_data']['avatar_url']));
|
||||
}
|
||||
|
||||
if (self::oauth_email_is_verified($oauth_data)) {
|
||||
AKSRT_Auth_Email_Verification::mark_email_verified($user_id);
|
||||
}
|
||||
|
||||
if ($obk !== '') {
|
||||
delete_transient(sanitize_text_field($obk));
|
||||
}
|
||||
|
||||
wp_set_auth_cookie($user_id, true);
|
||||
}
|
||||
|
||||
public static function get_email_verification_context(array $query): array {
|
||||
$invalid_payload = [
|
||||
'mode' => 'invalid',
|
||||
'user' => null,
|
||||
'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt'),
|
||||
'method' => '',
|
||||
'email' => '',
|
||||
'uid' => 0,
|
||||
'verified' => false,
|
||||
'access_key' => '',
|
||||
];
|
||||
|
||||
$user_id = isset($query['uid']) ? absint($query['uid']) : 0;
|
||||
if ($user_id <= 0) {
|
||||
return $invalid_payload;
|
||||
}
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user || is_wp_error($user)) {
|
||||
return $invalid_payload;
|
||||
}
|
||||
|
||||
$method = self::current_registration_verification_method();
|
||||
$email = (string) get_user_meta($user_id, self::EMAIL_TARGET_META, true);
|
||||
$token = sanitize_text_field((string) ($query['token'] ?? ''));
|
||||
$access = sanitize_text_field((string) ($query['vk'] ?? ''));
|
||||
|
||||
// 容错:meta 缺失时(例如历史账号被强制要求验证),回退到设置默认值与 user_email
|
||||
if (!in_array($method, ['link', 'code'], true)) {
|
||||
$method = self::current_registration_verification_method();
|
||||
}
|
||||
if ($email === '' || !is_email($email)) {
|
||||
$email = (string) $user->user_email;
|
||||
}
|
||||
$masked_email = self::masked_email($email);
|
||||
$has_access = self::has_valid_email_access_key($user_id, $access);
|
||||
|
||||
// token 路径:链接模式直接走 token 校验;不依赖 vk
|
||||
if ($token !== '') {
|
||||
if (AKSRT_Auth_Email_Verification::is_email_verified($user_id)) {
|
||||
return [
|
||||
'mode' => 'invalid',
|
||||
'user' => $user,
|
||||
'error' => __('该验证链接已失效或已使用,请重新发起验证流程', 'aksrt'),
|
||||
'method' => $has_access ? $method : '',
|
||||
'email' => $has_access ? $masked_email : '',
|
||||
'uid' => $has_access ? $user_id : 0,
|
||||
'verified' => true,
|
||||
'access_key' => $has_access ? $access : '',
|
||||
];
|
||||
}
|
||||
|
||||
$result = self::verify_email_token($user_id, $token);
|
||||
if (!empty($result['success'])) {
|
||||
return [
|
||||
'mode' => 'completed',
|
||||
'user' => $user,
|
||||
'error' => '',
|
||||
'method' => $method,
|
||||
'email' => $masked_email,
|
||||
'uid' => $user_id,
|
||||
'verified' => true,
|
||||
'redirect' => $result['redirect'] ?? home_url('/'),
|
||||
'message' => $result['message'] ?? '',
|
||||
'access_key' => $has_access ? $access : '',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'mode' => 'invalid',
|
||||
'user' => $user,
|
||||
'error' => $result['error'] ?? __('验证链接无效或已过期', 'aksrt'),
|
||||
'method' => $has_access ? $method : '',
|
||||
'email' => $has_access ? $masked_email : '',
|
||||
'uid' => $has_access ? $user_id : 0,
|
||||
'verified' => false,
|
||||
'access_key' => $has_access ? $access : '',
|
||||
];
|
||||
}
|
||||
|
||||
// 没有 token 也没有 vk:返回完全脱敏的通用错误,避免根据 uid 枚举账号状态
|
||||
if (!$has_access) {
|
||||
return $invalid_payload;
|
||||
}
|
||||
|
||||
if (!self::user_requires_email_verification($user)) {
|
||||
return [
|
||||
'mode' => 'verified',
|
||||
'user' => $user,
|
||||
'error' => '',
|
||||
'method' => '',
|
||||
'email' => self::masked_email((string) $user->user_email),
|
||||
'uid' => $user_id,
|
||||
'verified' => true,
|
||||
'access_key' => $access,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'mode' => $method === 'code' ? 'code' : 'pending',
|
||||
'user' => $user,
|
||||
'error' => '',
|
||||
'method' => $method,
|
||||
'email' => $masked_email ?: self::masked_email((string) $user->user_email),
|
||||
'uid' => $user_id,
|
||||
'verified' => false,
|
||||
'access_key' => $access,
|
||||
];
|
||||
}
|
||||
|
||||
public static function verify_email_code_submission(array $post): array {
|
||||
if (!wp_verify_nonce($post['_verify_email_nonce'] ?? '', 'aksrt_verify_email')) {
|
||||
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
|
||||
}
|
||||
|
||||
$user_id = isset($post['verify_uid']) ? absint($post['verify_uid']) : 0;
|
||||
$access = sanitize_text_field((string) ($post['vk'] ?? ''));
|
||||
$code = sanitize_text_field((string) ($post['email_code'] ?? ''));
|
||||
if ($user_id <= 0 || $code === '') {
|
||||
return ['success' => false, 'error' => __('请输入完整的邮箱验证码', 'aksrt')];
|
||||
}
|
||||
|
||||
if (!self::has_valid_email_access_key($user_id, $access)) {
|
||||
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_result = aksrt_require_captcha('verify_email', $post);
|
||||
if ($captcha_result !== true) {
|
||||
return ['success' => false, 'error' => $captcha_result];
|
||||
}
|
||||
}
|
||||
|
||||
return self::verify_email_code($user_id, $code);
|
||||
}
|
||||
|
||||
public static function resend_email_verification(array $post): array {
|
||||
if (!wp_verify_nonce($post['_verify_email_nonce'] ?? '', 'aksrt_verify_email')) {
|
||||
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
|
||||
}
|
||||
|
||||
$user_id = isset($post['verify_uid']) ? absint($post['verify_uid']) : 0;
|
||||
$access = sanitize_text_field((string) ($post['vk'] ?? ''));
|
||||
|
||||
// 必须先持有有效 vk 才允许重发,避免未登录用户用 uid 枚举触发邮件
|
||||
if ($user_id <= 0 || !self::has_valid_email_access_key($user_id, $access)) {
|
||||
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
|
||||
}
|
||||
|
||||
// IP 节流:每 IP 每 RESEND_IP_WINDOW 秒最多 RESEND_IP_MAX 次(跨账号合并计数)
|
||||
$ip = sanitize_text_field((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
|
||||
if ($ip !== '') {
|
||||
$ip_key = 'aksrt_email_resend_ip_' . md5($ip);
|
||||
$ip_attempts = (int) get_transient($ip_key);
|
||||
if ($ip_attempts >= self::RESEND_IP_MAX) {
|
||||
self::log_event('verify.resend.ip_throttled', ['uid' => $user_id, 'ip' => $ip]);
|
||||
return ['success' => false, 'error' => __('操作过于频繁,请稍后再试', 'aksrt')];
|
||||
}
|
||||
set_transient($ip_key, $ip_attempts + 1, self::RESEND_IP_WINDOW);
|
||||
}
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user || is_wp_error($user)) {
|
||||
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
|
||||
}
|
||||
|
||||
if (!self::user_requires_email_verification($user)) {
|
||||
return ['success' => false, 'error' => __('该账号已完成邮箱验证,无需重复发送', 'aksrt')];
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_result = aksrt_require_captcha('verify_email', $post);
|
||||
if ($captcha_result !== true) {
|
||||
return ['success' => false, 'error' => $captcha_result];
|
||||
}
|
||||
}
|
||||
|
||||
$method = self::current_registration_verification_method();
|
||||
$email = (string) get_user_meta($user_id, self::EMAIL_TARGET_META, true);
|
||||
|
||||
// 容错:meta 缺失时回退到设置默认值与 user_email(兼容历史账号 / meta 被外部清理的情况)
|
||||
if (!in_array($method, ['link', 'code'], true)) {
|
||||
$method = AKSRT_Settings::registration_verification_method();
|
||||
}
|
||||
if ($email === '' || !is_email($email)) {
|
||||
$email = (string) $user->user_email;
|
||||
}
|
||||
if ($email === '' || !is_email($email)) {
|
||||
return ['success' => false, 'error' => __('当前账号缺少有效邮箱,请联系管理员', 'aksrt')];
|
||||
}
|
||||
|
||||
$requested_at = (int) get_user_meta($user_id, self::EMAIL_REQUESTED_AT_META, true);
|
||||
if ($requested_at > 0 && (time() - $requested_at) < 60) {
|
||||
return ['success' => false, 'error' => __('操作过于频繁,请稍后再试', 'aksrt')];
|
||||
}
|
||||
|
||||
return self::begin_email_verification($user, $method, [
|
||||
'email' => $email,
|
||||
'obk' => (string) get_user_meta($user_id, self::EMAIL_OBK_META, true),
|
||||
'force' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
private static function user_requires_email_verification($user): bool {
|
||||
return AKSRT_Auth_Email_Verification::user_requires_email_verification($user);
|
||||
}
|
||||
|
||||
private static function mark_email_verified(int $user_id): void {
|
||||
AKSRT_Auth_Email_Verification::mark_email_verified($user_id);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 公开 API:管理员后台 / 内部业务调用(委托 AKSRT_Auth_Email_Verification)
|
||||
// ============================================================
|
||||
|
||||
public static function user_is_email_verified(int $user_id): bool {
|
||||
return AKSRT_Auth_Email_Verification::user_is_email_verified($user_id);
|
||||
}
|
||||
|
||||
public static function get_user_verification_state(int $user_id): array {
|
||||
return AKSRT_Auth_Email_Verification::get_user_verification_state($user_id);
|
||||
}
|
||||
|
||||
public static function admin_mark_verified(int $user_id, int $by_admin_id = 0): array {
|
||||
return AKSRT_Auth_Email_Verification::admin_mark_verified($user_id, $by_admin_id);
|
||||
}
|
||||
|
||||
public static function admin_unmark_verified(int $user_id, int $by_admin_id = 0): array {
|
||||
return AKSRT_Auth_Email_Verification::admin_unmark_verified($user_id, $by_admin_id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
<?php
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
trait AKSRT_Auth_Service_Part_1b {
|
||||
|
||||
public static function admin_send_verification(int $user_id, int $by_admin_id = 0): array {
|
||||
return AKSRT_Auth_Email_Verification::admin_send_verification($user_id, $by_admin_id);
|
||||
}
|
||||
|
||||
public static function backfill_legacy_users_as_verified(bool $force = false): int {
|
||||
return AKSRT_Auth_Email_Verification::backfill_legacy_users_as_verified($force);
|
||||
}
|
||||
|
||||
public static function run_async_backfill_batch(int $offset = 0): void {
|
||||
AKSRT_Auth_Email_Verification::run_async_backfill_batch($offset);
|
||||
}
|
||||
|
||||
private static function delete_user_account(int $user_id): void {
|
||||
if ($user_id <= 0) {
|
||||
return;
|
||||
}
|
||||
if (!function_exists('wp_delete_user')) {
|
||||
require_once ABSPATH . 'wp-admin/includes/user.php';
|
||||
}
|
||||
if (function_exists('wp_delete_user')) {
|
||||
wp_delete_user($user_id);
|
||||
}
|
||||
}
|
||||
|
||||
private static function oauth_email_is_verified(array $oauth_data): bool {
|
||||
$user_data = isset($oauth_data['user_data']) && is_array($oauth_data['user_data']) ? $oauth_data['user_data'] : $oauth_data;
|
||||
if (empty($user_data['email'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($user_data['email_verified'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$provider = sanitize_key((string) ($user_data['provider'] ?? $oauth_data['provider'] ?? ''));
|
||||
if ($provider === 'google' && !empty($user_data['email'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($provider === 'github' && isset($user_data['email_verified'])) {
|
||||
return !empty($user_data['email_verified']);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function begin_email_verification(WP_User $user, string $method, array $args = []): array {
|
||||
return AKSRT_Auth_Email_Verification::begin_email_verification($user, $method, $args);
|
||||
}
|
||||
|
||||
private static function verify_email_token(int $user_id, string $token): array {
|
||||
return AKSRT_Auth_Email_Verification::verify_email_token($user_id, $token);
|
||||
}
|
||||
|
||||
private static function verify_email_code(int $user_id, string $code): array {
|
||||
return AKSRT_Auth_Email_Verification::verify_email_code($user_id, $code);
|
||||
}
|
||||
|
||||
public static function get_reset_context(array $query): array {
|
||||
if (!empty($query['key']) || !empty($query['login']) || !empty($query['legacy'])) {
|
||||
return [
|
||||
'mode' => 'invalid',
|
||||
'user' => null,
|
||||
'error' => __('旧的重置密码链接已失效,请重新申请重置密码。', 'aksrt'),
|
||||
'token' => '',
|
||||
'user_id'=> 0,
|
||||
];
|
||||
}
|
||||
|
||||
$token = sanitize_text_field($query['token'] ?? '');
|
||||
$user_id = isset($query['uid']) ? absint($query['uid']) : 0;
|
||||
|
||||
if ($token === '' || $user_id <= 0) {
|
||||
return [
|
||||
'mode' => 'request',
|
||||
'user' => null,
|
||||
'error' => '',
|
||||
'token' => '',
|
||||
'user_id'=> 0,
|
||||
];
|
||||
}
|
||||
|
||||
$user = self::validate_reset_token($user_id, $token);
|
||||
if (is_wp_error($user)) {
|
||||
return [
|
||||
'mode' => 'invalid',
|
||||
'user' => null,
|
||||
'error' => $user->get_error_message(),
|
||||
'token' => $token,
|
||||
'user_id'=> $user_id,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'mode' => 'reset',
|
||||
'user' => $user,
|
||||
'error' => '',
|
||||
'token' => $token,
|
||||
'user_id'=> $user_id,
|
||||
];
|
||||
}
|
||||
|
||||
public static function request_password_reset(array $post): array {
|
||||
if (!wp_verify_nonce($post['_reset_nonce'] ?? '', 'aksrt_reset_password')) {
|
||||
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_result = aksrt_require_captcha('lostpassword', $post);
|
||||
if ($captcha_result !== true) {
|
||||
return ['success' => false, 'error' => $captcha_result];
|
||||
}
|
||||
}
|
||||
|
||||
$raw_login = trim((string) ($post['user_login'] ?? ''));
|
||||
if ($raw_login === '') {
|
||||
return ['success' => false, 'error' => __('请输入邮箱地址或用户名', 'aksrt')];
|
||||
}
|
||||
|
||||
$user = null;
|
||||
if (is_email($raw_login)) {
|
||||
$user = get_user_by('email', $raw_login);
|
||||
} else {
|
||||
$user = get_user_by('login', sanitize_user($raw_login, true));
|
||||
}
|
||||
|
||||
if (!$user || is_wp_error($user)) {
|
||||
return ['success' => true, 'message' => __('如果该账号存在,重置邮件已发送到注册邮箱', 'aksrt')];
|
||||
}
|
||||
|
||||
$token = self::create_reset_token($user->ID);
|
||||
$sent = self::send_password_reset_email($user, $token);
|
||||
|
||||
if (!$sent) {
|
||||
return ['success' => false, 'error' => __('重置邮件发送失败,请稍后再试', 'aksrt')];
|
||||
}
|
||||
|
||||
return ['success' => true, 'message' => __('如果该账号存在,重置邮件已发送到注册邮箱', 'aksrt')];
|
||||
}
|
||||
|
||||
public static function reset_password_with_token(array $post): array {
|
||||
if (!wp_verify_nonce($post['_setpw_nonce'] ?? '', 'aksrt_set_password')) {
|
||||
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
|
||||
}
|
||||
|
||||
$token = sanitize_text_field($post['reset_token'] ?? '');
|
||||
$user_id = isset($post['reset_uid']) ? absint($post['reset_uid']) : 0;
|
||||
$pass1 = (string) ($post['pass1'] ?? '');
|
||||
$pass2 = (string) ($post['pass2'] ?? '');
|
||||
|
||||
$user = self::validate_reset_token($user_id, $token);
|
||||
if (is_wp_error($user)) {
|
||||
return ['success' => false, 'error' => $user->get_error_message(), 'mode' => 'invalid'];
|
||||
}
|
||||
|
||||
$password_check = self::validate_password($pass1, $pass2);
|
||||
if ($password_check !== true) {
|
||||
return ['success' => false, 'error' => $password_check, 'mode' => 'reset'];
|
||||
}
|
||||
|
||||
wp_set_password($pass1, $user_id);
|
||||
self::clear_reset_token($user_id);
|
||||
|
||||
return ['success' => true, 'message' => __('密码已设置成功!', 'aksrt')];
|
||||
}
|
||||
|
||||
private static function create_reset_token(int $user_id): string {
|
||||
$token = wp_generate_password(48, false, false);
|
||||
update_user_meta($user_id, self::RESET_TOKEN_HASH_META, hash('sha256', $token));
|
||||
update_user_meta($user_id, self::RESET_TOKEN_EXPIRES_META, time() + self::RESET_TOKEN_TTL);
|
||||
update_user_meta($user_id, self::RESET_TOKEN_REQUESTED_AT, time());
|
||||
return $token;
|
||||
}
|
||||
|
||||
private static function validate_reset_token(int $user_id, string $token) {
|
||||
if ($user_id <= 0 || $token === '') {
|
||||
return new WP_Error('invalid_reset_token', __('重置链接无效或已过期', 'aksrt'));
|
||||
}
|
||||
|
||||
$user = get_userdata($user_id);
|
||||
if (!$user || is_wp_error($user)) {
|
||||
return new WP_Error('invalid_reset_token', __('重置链接无效或已过期', 'aksrt'));
|
||||
}
|
||||
|
||||
$stored_hash = (string) get_user_meta($user_id, self::RESET_TOKEN_HASH_META, true);
|
||||
$expires_at = (int) get_user_meta($user_id, self::RESET_TOKEN_EXPIRES_META, true);
|
||||
$token_hash = hash('sha256', $token);
|
||||
|
||||
if ($stored_hash === '' || $expires_at <= time() || !hash_equals($stored_hash, $token_hash)) {
|
||||
return new WP_Error('invalid_reset_token', __('重置链接无效或已过期', 'aksrt'));
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private static function clear_reset_token(int $user_id): void {
|
||||
delete_user_meta($user_id, self::RESET_TOKEN_HASH_META);
|
||||
delete_user_meta($user_id, self::RESET_TOKEN_EXPIRES_META);
|
||||
delete_user_meta($user_id, self::RESET_TOKEN_REQUESTED_AT);
|
||||
}
|
||||
|
||||
private static function send_password_reset_email(WP_User $user, string $token): bool {
|
||||
$reset_url = aksrt_get_reset_password_url($token, $user->ID);
|
||||
$site_name = get_bloginfo('name');
|
||||
$default_subject = '[' . $site_name . '] ' . __('重置密码', 'aksrt');
|
||||
$default_content = sprintf(
|
||||
__("您好 %s,\n\n有人请求重置您在 %s 的密码。\n\n请点击以下链接设置新密码:\n\n%s\n\n如果这不是您本人的操作,请忽略此邮件,您的密码不会被更改。", 'aksrt'),
|
||||
$user->user_login, $site_name, $reset_url
|
||||
);
|
||||
|
||||
return AKSRT_Settings::send_event_email(
|
||||
'password_reset', $user->user_email, $default_subject, $default_content,
|
||||
['{username}' => $user->user_login, '{site_name}' => $site_name, '{url}' => $reset_url]
|
||||
);
|
||||
}
|
||||
|
||||
/* =================================================================
|
||||
* 手机号认证(注册 / 登录 / 绑定)
|
||||
* 对齐 zibll 的 signup_captcha / signin_captcha / bind_phone_captcha
|
||||
* ================================================================= */
|
||||
|
||||
/**
|
||||
* 手机号注册
|
||||
*
|
||||
* @param array $post POST 数据 (phone, code, password, invite_code)
|
||||
* @return array{success: bool, user_id?: int, error?: string, redirect?: string}
|
||||
*/
|
||||
public static function register_by_phone(array $post): array {
|
||||
$phone = sanitize_text_field((string) ($post['phone'] ?? ''));
|
||||
$code = sanitize_text_field((string) ($post['code'] ?? ''));
|
||||
$pass = (string) ($post['password'] ?? '');
|
||||
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
return ['success' => false, 'error' => __('手机号格式不正确', 'aksrt')];
|
||||
}
|
||||
if (empty($code)) {
|
||||
return ['success' => false, 'error' => __('请输入验证码', 'aksrt')];
|
||||
}
|
||||
if (strlen($pass) < 6) {
|
||||
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);
|
||||
if (empty($verify_result['success'])) {
|
||||
return ['success' => false, 'error' => $verify_result['message'] ?? __('验证码不正确', 'aksrt')];
|
||||
}
|
||||
} else {
|
||||
return ['success' => false, 'error' => __('短信服务不可用', 'aksrt')];
|
||||
}
|
||||
|
||||
// 检查手机号是否已注册
|
||||
$existing_user = self::get_user_by_phone($phone);
|
||||
if ($existing_user) {
|
||||
return ['success' => false, 'error' => __('该手机号已注册', 'aksrt')];
|
||||
}
|
||||
|
||||
// 生成用户名(phone_ + 后4位)
|
||||
$username = 'phone_' . substr($phone, -4);
|
||||
$suffix = 1;
|
||||
$base = $username;
|
||||
while (username_exists($username)) {
|
||||
$username = $base . '_' . $suffix;
|
||||
$suffix++;
|
||||
}
|
||||
|
||||
// 创建用户
|
||||
$user_id = wp_create_user($username, $pass, $phone . '@sms.aksrt.local');
|
||||
if (is_wp_error($user_id)) {
|
||||
return ['success' => false, 'error' => $user_id->get_error_message()];
|
||||
}
|
||||
|
||||
// 绑定手机号
|
||||
update_user_meta($user_id, 'aksrt_phone', $phone);
|
||||
update_user_meta($user_id, 'aksrt_phone_verified', '1');
|
||||
|
||||
// 邮箱标记为已验证(手机注册等同于已验证)
|
||||
update_user_meta($user_id, 'aksrt_email_verified', '1');
|
||||
|
||||
// 处理邀请码
|
||||
$invite_code = sanitize_key((string) ($post['invite_code'] ?? ''));
|
||||
if ($invite_code !== '' && class_exists('AKSRT_Invite')) {
|
||||
AKSRT_Invite::get_instance()->handle_registration_invite($user_id, $invite_code);
|
||||
}
|
||||
|
||||
// 自动登录
|
||||
wp_set_current_user($user_id);
|
||||
wp_set_auth_cookie($user_id, true);
|
||||
|
||||
$redirect = function_exists('aksrt_get_user_center_url')
|
||||
? aksrt_get_user_center_url()
|
||||
: home_url('/');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'user_id' => $user_id,
|
||||
'redirect' => $redirect,
|
||||
'message' => __('注册成功', 'aksrt'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号登录(密码登录)
|
||||
*
|
||||
* @param array $post (phone, password)
|
||||
* @return array
|
||||
*/
|
||||
public static function login_by_phone_password(array $post): array {
|
||||
$phone = sanitize_text_field((string) ($post['phone'] ?? ''));
|
||||
$pass = (string) ($post['password'] ?? '');
|
||||
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
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')];
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!wp_check_password($pass, $user->data->user_pass, $user->ID)) {
|
||||
return ['success' => false, 'error' => __('密码不正确', 'aksrt')];
|
||||
}
|
||||
|
||||
wp_set_current_user($user->ID);
|
||||
wp_set_auth_cookie($user->ID, true);
|
||||
|
||||
$redirect = function_exists('aksrt_get_user_center_url')
|
||||
? aksrt_get_user_center_url()
|
||||
: home_url('/');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'user_id' => $user->ID,
|
||||
'redirect' => $redirect,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号免密登录(SMS 验证码登录)
|
||||
*
|
||||
* @param array $post (phone, code)
|
||||
* @return array
|
||||
*/
|
||||
public static function login_by_phone_code(array $post): array {
|
||||
$phone = sanitize_text_field((string) ($post['phone'] ?? ''));
|
||||
$code = sanitize_text_field((string) ($post['code'] ?? ''));
|
||||
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
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);
|
||||
if (empty($verify_result['success'])) {
|
||||
return ['success' => false, 'error' => $verify_result['message'] ?? __('验证码不正确', 'aksrt')];
|
||||
}
|
||||
} else {
|
||||
return ['success' => false, 'error' => __('短信服务不可用', 'aksrt')];
|
||||
}
|
||||
|
||||
$user = self::get_user_by_phone($phone);
|
||||
if (!$user) {
|
||||
return ['success' => false, 'error' => __('该手机号未注册', 'aksrt')];
|
||||
}
|
||||
|
||||
wp_set_current_user($user->ID);
|
||||
wp_set_auth_cookie($user->ID, true);
|
||||
|
||||
$redirect = function_exists('aksrt_get_user_center_url')
|
||||
? aksrt_get_user_center_url()
|
||||
: home_url('/');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'user_id' => $user->ID,
|
||||
'redirect' => $redirect,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机号到当前用户
|
||||
*
|
||||
* @param array $post (phone, code)
|
||||
* @return array
|
||||
*/
|
||||
public static function bind_phone(array $post): array {
|
||||
if (!is_user_logged_in()) {
|
||||
return ['success' => false, 'error' => __('请先登录', 'aksrt')];
|
||||
}
|
||||
|
||||
$phone = sanitize_text_field((string) ($post['phone'] ?? ''));
|
||||
$code = sanitize_text_field((string) ($post['code'] ?? ''));
|
||||
|
||||
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
||||
return ['success' => false, 'error' => __('手机号格式不正确', 'aksrt')];
|
||||
}
|
||||
|
||||
if (function_exists('aksrt_require_captcha')) {
|
||||
$captcha_result = aksrt_require_captcha('bind_phone', $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);
|
||||
if (empty($verify_result['success'])) {
|
||||
return ['success' => false, 'error' => $verify_result['message'] ?? __('验证码不正确', 'aksrt')];
|
||||
}
|
||||
} else {
|
||||
return ['success' => false, 'error' => __('短信服务不可用', 'aksrt')];
|
||||
}
|
||||
|
||||
// 检查手机号是否已被其他用户绑定
|
||||
$existing = self::get_user_by_phone($phone);
|
||||
if ($existing && (int) $existing->ID !== get_current_user_id()) {
|
||||
return ['success' => false, 'error' => __('该手机号已被其他账号绑定', 'aksrt')];
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
update_user_meta($user_id, 'aksrt_phone', $phone);
|
||||
update_user_meta($user_id, 'aksrt_phone_verified', '1');
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'message' => __('手机号绑定成功', 'aksrt'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机号查找用户
|
||||
*/
|
||||
private static function get_user_by_phone(string $phone): ?WP_User {
|
||||
$users = get_users([
|
||||
'meta_key' => 'aksrt_phone',
|
||||
'meta_value' => $phone,
|
||||
'number' => 1,
|
||||
'fields' => 'all',
|
||||
]);
|
||||
return !empty($users) ? $users[0] : null;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user