用戶 A 分享商品連結給 B,B 點開未登入 → 導向登入頁 → 登入成功 → **跳回首頁**(而非商品頁)。體驗極差,轉化率直線下降。
<?php
// check_login.php - 所有需登入頁面頂部 require
session_start();
if (!isset($_SESSION['login_ok'])) {
// 記錄完整請求 URI(含查詢字串)
$_SESSION['redirect_url'] = $_SERVER['REQUEST_URI'];
// 彈窗提示並跳轉登入頁
echo '<script>';
echo 'alert("請先登入才能訪問該頁面");';
echo 'location.href = "/login.php";';
echo '</script>';
exit;
}
?>
<?php
// login.php
session_start();
// ... 此處執行帳密驗證、驗證碼、OAuth 等邏輯 ...
$loginSuccess = true; // 假設驗證通過
if ($loginSuccess) {
$_SESSION['login_ok'] = true;
$_SESSION['user_id'] = $userId;
// 取得原始目標頁,無則預設首頁
$targetUrl = $_SESSION['redirect_url'] ?? '/index.php';
unset($_SESSION['redirect_url']); // 用完即刪,避免殘留
// 安全過濾:防止開放重定向攻擊
if (!preg_match('#^(/|https?://' . preg_quote($_SERVER['HTTP_HOST'], '#') . ')#', $targetUrl)) {
$targetUrl = '/index.php';
}
// 方式一:Meta Refresh(兼容性最好)
echo "<meta http-equiv=\"refresh\" content=\"0.5;url=$targetUrl\">";
echo "<script>location.href = '$targetUrl';</script>"; // 雙重保障
exit;
}
?>
| 風險 | 對策 |
|---|---|
| 開放重定向 | 白名單驗證 $targetUrl 僅允許相對路徑或同域絕對路徑 |
| Session Fixation | 登入成功後 session_regenerate_id(true) |
| CSRF | 登入表單加入 csrf_token 驗證 |
| 敏感頁面殘留 | 登出時清除 redirect_url,避免登出後誤導 |
<?php
// API 端點:/api/login
header('Content-Type: application/json');
session_start();
// ... 驗證邏輯 ...
if ($success) {
$_SESSION['login_ok'] = true;
$target = $_SESSION['redirect_url'] ?? '/';
unset($_SESSION['redirect_url']);
echo json_encode(['code' => 0, 'redirect' => $target]);
exit;
}
echo json_encode(['code' => 401, 'msg' => '帳號或密碼錯誤']);
?>
前端收到 redirect 直接 router.push() 或 location.href,無刷新跳轉。
| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| Session 記錄 URI | 簡單、可靠、支援完整查詢字串 | 需 Session、多標籤頁可能互相干擾 | 傳統多頁應用(MPA) |
Hidden Input 傳遞 redirect | 無 Session 依賴、多標籤隔離 | 需修改所有登入連結帶參數 | 無 Session 環境、靜態站點 |
| Referer Header | 零代碼侵入 | 不可靠(可偽造、隱私模式無 Referer) | 輔助備選,不可作主流 |
JWT 携帶 redirect claim | 無狀態、跨域支援 | Token 體積大、需簽名驗證 | 微服務、SSO、前後端分離 |
require 'check_login.php'action 指向處理邏輯,而非自身redirect_urlsession_regenerate_id() 防 Session Fixationredirect_from 供分析