典型註冊流程:協議頁 → 輸入頁 → 結果頁。常見問題:1) 直接訪問輸入頁跳過協議;2) 結果頁刷新導致重複寫入;3) 返回修改後再次提交造成資料不一致。本文提供基於 Session 令牌與 POST-Redirect-GET 模式的完整解決方案。
$_SESSION['step'])。<?php
session_start();
// 禁止瀏覽器快取,確保回退時重新請求伺服器
header('Expires: ' . date('D, d M Y H:i:s', mktime(0,0,0,1,1,2000)) . ' GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: private, no-cache, must-revalidate');
header('Pragma: no-cache');
?>
<?php
require_once "config.inc.php";
// 步驟 3:處理表單提交
if (isset($_SESSION['step2_token']) && isset($_POST['step2_token'])) {
// 驗證 Token 防重複提交
if ($_POST['step2_token'] !== $_SESSION['step2_token']) {
die("非法請求或重複提交");
}
unset($_SESSION['step2_token']); // 一次性 Token 即時失效
// 驗證輸入
$errors = [];
$username = trim($_POST['username'] ?? '');
$address = trim($_POST['address'] ?? '');
if ($username === '') $errors[] = "用戶名不可為空";
if ($address === '') $errors[] = "地址不可為空";
if ($errors) {
// 驗證失敗:回步驟 2 頁面,攜帶錯誤訊息
$_SESSION['form_errors'] = $errors;
$_SESSION['form_data'] = $_POST; // 回填資料
header("Location: register.php?step=2");
exit;
}
// 業務邏輯:寫入資料庫等...
// $db->insert('users', ['username'=>$username, 'address'=>$address]);
// 成功:清理 Session 步驟狀態,重導向至步驟 3 成功頁
unset($_SESSION['step1_token'], $_SESSION['step2_token'], $_SESSION['form_errors'], $_SESSION['form_data']);
header("Location: register.php?step=3");
exit;
}
// 步驟 2:顯示輸入表單
if (isset($_SESSION['step1_token']) && isset($_GET['step']) && $_GET['step'] == '2') {
// 發放步驟 2 專用 Token
$_SESSION['step2_token'] = bin2hex(random_bytes(16));
include 'step2.html';
exit;
}
// 步驟 1:顯示協議頁
// 發放步驟 1 專用 Token
$_SESSION['step1_token'] = bin2hex(random_bytes(16));
include 'step1.html';
?>
<form method="get" action="register.php">
<input type="hidden" name="step" value="2">
<input type="hidden" name="step1_token" value="<?= $_SESSION['step1_token'] ?>">
<p>用戶協議內容...</p>
<button type="submit">我同意,進入註冊</button>
</form>
<?php
$errors = $_SESSION['form_errors'] ?? [];
$data = $_SESSION['form_data'] ?? [];
unset($_SESSION['form_errors'], $_SESSION['form_data']); // 讀取後即清除
?>
<?php if ($errors): ?>
<div style="color:red;"><ul><?php foreach($errors as $e) echo "<li>$e</li>"; ?></ul></div>
<?php endif; ?>
<form method="post" action="register.php">
<input type="hidden" name="step2_token" value="<?= $_SESSION['step2_token'] ?>">
<p>用戶名:<input name="username" value="<?= htmlspecialchars($data['username'] ?? '') ?>"></p>
<p>地址:<input name="address" value="<?= htmlspecialchars($data['address'] ?? '') ?>"></p>
<button type="submit">提交註冊</button>
</form>
<h1>註冊成功!</h1>
<p>歡迎加入,<?= htmlspecialchars($_SESSION['registered_username'] ?? '') ?>。</p>
<p><a href="index.php">返回首頁</a></p>
header("Location: ...") 導向 GET,瀏覽器歷史記錄無 POST,刷新無害。session_start() 前輸出,強制瀏覽器回退時重新請求,獲得最新狀態(如 Token 已失效)。disabled),雙重保障。