Files
wordpress-theme-aksrt/inc/classes/auth-traits/trait-aksrt-auth-service-part-1b.php
T

448 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('lostpassword')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
}
$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')];
}
// 验证 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')];
}
$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')];
}
// 验证 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')];
}
// 验证 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;
}
}