$provider, 'agent_back_url' => $back_url, 'action' => $action, 'nonce' => $nonce, 'uid' => $uid, 'ts' => time(), ]; $params['sign'] = $this->sign($params, $secret); $server_url = rtrim($server_url, '/'); return $server_url . '/oauth/proxy/' . $provider . '?' . http_build_query($params); } /** * 处理代理回调(从服务端重定向回来) */ public function handle_proxy_callback(): void { if (!get_query_var('aksrt_proxy_callback')) { return; } $secret = AKSRT_Settings::oauth_proxy_secret(); if (empty($secret)) { wp_die(__('代理登录配置错误', 'aksrt')); } $sign = sanitize_text_field($_GET['sign'] ?? ''); $provider = sanitize_key($_GET['provider'] ?? ''); $action = sanitize_key($_GET['action'] ?? 'login'); if (empty($sign) || empty($provider)) { wp_die(__('代理回调参数无效', 'aksrt')); } // 验证签名 $verify_params = $_GET; unset($verify_params['sign']); if (!$this->verify_sign($verify_params, $secret, $sign)) { wp_die(__('代理回调签名验证失败', 'aksrt')); } // 验证时间戳(5分钟内有效) $ts = (int) ($_GET['ts'] ?? 0); if (abs(time() - $ts) > 300) { wp_die(__('代理回调已过期,请重新登录', 'aksrt')); } // 提取用户数据 $provider_id = sanitize_text_field($_GET['provider_id'] ?? ''); if (empty($provider_id)) { wp_die(__('代理回调缺少用户标识', 'aksrt')); } $user_data = [ 'provider' => $provider, 'provider_id' => $provider_id, 'username' => sanitize_user($_GET['username'] ?? '', true), 'name' => sanitize_text_field($_GET['name'] ?? ''), 'email' => sanitize_email($_GET['email'] ?? ''), 'email_verified' => !empty($_GET['email_verified']), 'avatar_url' => esc_url_raw($_GET['avatar_url'] ?? ''), 'bio' => sanitize_text_field($_GET['bio'] ?? ''), ]; if ($action === 'bind') { $this->handle_proxy_bind($provider, $user_data); return; } $this->handle_proxy_login($provider, $user_data); } /** * 处理代理登录 */ private function handle_proxy_login(string $provider, array $user_data): void { $user = $this->find_or_create_user_from_proxy($provider, $user_data); // 未匹配已有账号 → 跳转绑定页 if (is_wp_error($user) && $user->get_error_code() === 'oauth_no_match') { $error_data = $user->get_error_data(); $obk = 'aksrt_ob_' . wp_generate_password(24, false); set_transient($obk, [ 'provider' => $error_data['provider'], 'provider_id' => $error_data['user_data']['provider_id'] ?? '', 'user_data' => $error_data['user_data'], 'token' => [], ], 15 * MINUTE_IN_SECONDS); $bind_url = aksrt_get_bind_url(); wp_safe_redirect(add_query_arg('obk', $obk, $bind_url)); exit; } if (is_wp_error($user)) { wp_die(__('登录失败:', 'aksrt') . esc_html($user->get_error_message())); } // 登录成功 wp_set_auth_cookie($user->ID, true); $redirect_url = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : home_url('/'); wp_safe_redirect(add_query_arg(['oauth_action' => 'login', 'oauth_bound' => $provider], $redirect_url)); exit; } /** * 处理代理绑定(已登录用户绑定第三方账号) */ private function handle_proxy_bind(string $provider, array $user_data): void { $current_uid = get_current_user_id(); if (!$current_uid) { wp_safe_redirect(aksrt_get_login_url()); exit; } // 检查是否已被其他用户占用 $existing = $this->find_user_by_provider_meta($provider, $user_data['provider_id']); if ($existing && (int) $existing->ID !== $current_uid) { wp_die(__('该第三方账号已被其他用户绑定', 'aksrt')); } $this->bind_provider_to_user($current_uid, $provider, $user_data); $redirect_url = function_exists('aksrt_get_user_center_url') ? add_query_arg(['oauth_action' => 'bind', 'oauth_bound' => $provider], aksrt_get_user_center_url('settings')) : home_url('/'); wp_safe_redirect($redirect_url); exit; } /** * 查找或创建用户(代理模式) * * @return WP_User|WP_Error */ private function find_or_create_user_from_proxy(string $provider, array $user_data) { $email = $user_data['email'] ?? ''; // 1. 通过 provider_id 查找 if (!empty($user_data['provider_id'])) { $existing = $this->find_user_by_provider_meta($provider, $user_data['provider_id']); if ($existing instanceof WP_User) { $this->ensure_binding_meta($existing->ID, $provider, $user_data); return $existing; } } // 2. 通过邮箱查找 if (!empty($email) && is_email($email)) { $existing = get_user_by('email', $email); if ($existing && !$existing instanceof WP_Error) { $this->ensure_binding_meta($existing->ID, $provider, $user_data); return $existing; } } // 3. 未匹配 return new WP_Error('oauth_no_match', __('需要绑定已有账号', 'aksrt'), [ 'provider' => $provider, 'user_data' => $user_data, ]); } /** * 确保绑定 meta 存在 */ private function ensure_binding_meta(int $user_id, string $provider, array $user_data): void { update_user_meta($user_id, "aksrt_{$provider}_id", $user_data['provider_id'] ?? ''); $bindings = get_user_meta($user_id, 'aksrt_oauth_bindings', true) ?: []; if (!is_array($bindings)) { $bindings = []; } if (!in_array($provider, $bindings, true)) { $bindings[] = $provider; update_user_meta($user_id, 'aksrt_oauth_bindings', $bindings); } if (!empty($user_data['avatar_url'])) { update_user_meta($user_id, 'aksrt_oauth_avatar', esc_url_raw($user_data['avatar_url'])); } } /** * 绑定第三方账号到用户 */ private function bind_provider_to_user(int $user_id, string $provider, array $user_data): void { if (!empty($user_data['provider_id'])) { update_user_meta($user_id, "aksrt_{$provider}_id", $user_data['provider_id']); } $bindings = get_user_meta($user_id, 'aksrt_oauth_bindings', true) ?: []; if (!is_array($bindings)) { $bindings = []; } if (!in_array($provider, $bindings, true)) { $bindings[] = $provider; update_user_meta($user_id, 'aksrt_oauth_bindings', $bindings); } if (!empty($user_data['avatar_url'])) { update_user_meta($user_id, 'aksrt_oauth_avatar', esc_url_raw($user_data['avatar_url'])); } } /** * 通过 provider meta 查找用户 */ private function find_user_by_provider_meta(string $provider, string $provider_id): ?WP_User { if (empty($provider_id)) { return null; } $users = get_users([ 'meta_key' => "aksrt_{$provider}_id", 'meta_value' => $provider_id, 'number' => 1, ]); return !empty($users[0]) ? $users[0] : null; } /** * 生成 HMAC-SHA256 签名 */ private function sign(array $data, string $secret): string { unset($data['sign']); ksort($data); $payload = http_build_query($data); return hash_hmac('sha256', $payload, $secret); } /** * 验证 HMAC-SHA256 签名 */ private function verify_sign(array $data, string $secret, string $expected_sign): bool { $computed = $this->sign($data, $secret); return hash_equals($computed, $expected_sign); } } return AKSRT_OAuth_Proxy_Client::get_instance();