From 6e71008d00d18f151bc211fa85ff8fa8afa7a4cf Mon Sep 17 00:00:00 2001 From: unknown Date: Sat, 25 Jul 2026 15:09:58 +0800 Subject: [PATCH] fix(auth): logout without /logout rewrite (avoid Apache 404) --- inc/modules/user-parts/urls.php | 21 ++++++++++++++--- src/AKSRT/Auth/Router.php | 41 +++++++++++++++++++++++++++++---- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/inc/modules/user-parts/urls.php b/inc/modules/user-parts/urls.php index 8cff7ff6..a1b42b56 100644 --- a/inc/modules/user-parts/urls.php +++ b/inc/modules/user-parts/urls.php @@ -261,10 +261,25 @@ function aksrt_get_bind_url($transient_key = '') { endif; if (!function_exists('aksrt_get_logout_url')) : -/** 获取退出登录 URL */ +/** + * 获取退出登录 URL。 + * + * Prefer query-arg form so logout works even when /logout/ rewrite is missing + * (Apache would 404 the pretty path before WordPress runs). Pretty /logout/ + * remains registered as a convenience alias. + * + * @param string $redirect Redirect after logout. + */ function aksrt_get_logout_url($redirect = '') { - $target = $redirect ?: home_url('/'); - $url = add_query_arg('redirect_to', rawurlencode($target), home_url('/logout/')); + $target = is_string($redirect) && $redirect !== '' ? $redirect : home_url('/'); + // home_url('/?aksrt_logout=1') always hits index.php without rewrite rules. + $url = add_query_arg( + [ + 'aksrt_logout' => '1', + 'redirect_to' => $target, + ], + home_url('/') + ); return wp_nonce_url($url, 'aksrt-logout'); } endif; diff --git a/src/AKSRT/Auth/Router.php b/src/AKSRT/Auth/Router.php index e0400e4d..d734d323 100644 --- a/src/AKSRT/Auth/Router.php +++ b/src/AKSRT/Auth/Router.php @@ -85,7 +85,7 @@ final class Router { } public function maybe_flush_rewrite_rules(): void { - $rewrite_version = AKSRT_VERSION . ':sign-router-v1'; + $rewrite_version = AKSRT_VERSION . ':sign-router-v2-logout'; if (get_option('aksrt_auth_routes_flushed') !== $rewrite_version) { flush_rewrite_rules(false); update_option('aksrt_auth_routes_flushed', $rewrite_version); @@ -187,7 +187,7 @@ final class Router { } public function handle_logout_route(): void { - if (!get_query_var('aksrt_logout')) { + if (!$this->is_logout_request()) { return; } @@ -197,13 +197,44 @@ final class Router { } $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field((string) $_GET['_wpnonce']) : ''; - if (!wp_verify_nonce($nonce, 'aksrt-logout')) { - wp_die(__('退出登录请求无效,请重试。', 'aksrt')); + // Accept theme nonce and core log-out nonce (wp_logout_url default). + $nonce_ok = ($nonce !== '' && ( + wp_verify_nonce($nonce, 'aksrt-logout') + || wp_verify_nonce($nonce, 'log-out') + )); + if (!$nonce_ok) { + wp_die(esc_html__('退出登录请求无效,请重试。', 'aksrt'), esc_html__('退出登录', 'aksrt'), ['response' => 403]); } $redirect_to = isset($_GET['redirect_to']) ? wp_unslash((string) $_GET['redirect_to']) : home_url('/'); + $redirect_to = wp_validate_redirect($redirect_to, home_url('/')); + wp_logout(); - wp_safe_redirect($redirect_to ?: home_url('/')); + wp_safe_redirect($redirect_to !== '' ? $redirect_to : home_url('/')); exit; } + + /** + * Detect logout via rewrite query var, GET flag, or pretty path. + */ + private function is_logout_request(): bool { + if ((string) get_query_var('aksrt_logout') !== '' && (string) get_query_var('aksrt_logout') !== '0') { + return true; + } + if (isset($_GET['aksrt_logout']) && (string) $_GET['aksrt_logout'] !== '' && (string) $_GET['aksrt_logout'] !== '0') { + return true; + } + + $request_uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : ''; + if ($request_uri === '') { + return false; + } + $path = (string) wp_parse_url($request_uri, PHP_URL_PATH); + $home_path = (string) wp_parse_url(home_url('/'), PHP_URL_PATH); + if ($home_path !== '' && $home_path !== '/' && str_starts_with($path, $home_path)) { + $path = (string) substr($path, strlen(rtrim($home_path, '/'))); + } + $path = '/' . trim($path, '/'); + return $path === '/logout'; + } }