首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 防止表單重複提交:Session Token + PRG 模式完整解決方案

     編輯:PHP 綜合知識

問題根源

用戶提交表單後按 F5 刷新,瀏覽器會重發 POST 請求,導致:

  • 重複下單、重複扣款
  • 重複發帖、評論刷屏
  • 資料庫髒數據、業務邏輯錯亂

核心原理:Server-Side Token 去重

利用 Session 在服務端記錄「已處理的 Token」,每次提交攜帶新 Token,處理成功後標記舊 Token 失效、生成新 Token。刷新時舊 Token 已失效,直接拒絕。

最小可用代碼(單文件演示)

重新載入');
    }

    // 業務邏輯(這裡只演示輸出)
    echo "

提交成功!數據:

";
    print_r($_POST);
    echo "
"; // 3. 關鍵:消耗 Token,生成新 Token(防刷新重複) $_SESSION['form_token'] = bin2hex(random_bytes(16)); // 4. PRG 模式:重導向到 GET 頁面,徹底杜絕重發 header('Location: ' . $_SERVER['PHP_SELF'] . '?success=1'); exit; } ?> 防重複提交演示
✅ 處理完成,刷新頁面不會重複提交。


進階:Token 存儲優化(支援多標籤頁)

單一 Token 會導致多標籤頁互相衝突。改用「Token 池」:

// 生成 Token 並存入池(最多保留 5 個)
$_SESSION['token_pool'] = $_SESSION['token_pool'] ?? [];
$_SESSION['token_pool'][] = $newToken = bin2hex(random_bytes(16));
if (count($_SESSION['token_pool']) > 5) array_shift($_SESSION['token_pool']);

// 驗證時從池中查找並移除
$submitted = $_POST['_token'] ?? '';
$pool = &$_SESSION['token_pool'];
$key = array_search($submitted, $pool, true);
if ($key === false) die('Token 無效或已過期');
unset($pool[$key]); // 一次性消耗
$pool = array_values($pool); // 重排索引

PRG (Post-Redirect-Get) 模式詳解

  1. 用戶 POST 提交
  2. 服務端處理業務、寫入資料庫
  3. 服務端返回 302/303 Location: /success-page
  4. 瀏覽器自動 GET 請求成功頁
  5. 此時用戶刷新(F5)只會重發 GET,不會重發 POST

黃金法則: 所有修改數據的操作(增/刪/改)必須走 PRG;純查詢(搜尋、列表、詳情)走 GET。

前端輔助:提交後禁用按鈕

常見誤區與避坑

誤區正確做法
只用 JS 禁用按鈕JS 可被繞過,必須有服務端 Token 驗證
用 $_SESSION['submitted'] = true 簡單標記無法支援多標籤、多表單並發,需用 Token 池
驗證通過後不重導向,直接輸出結果頁違反 PRG,用戶刷新仍會重發 POST
Token 存入資料庫過度設計,Session 存儲已足夠(單機/集群共享 Session 即可)

框架整合提示

  • Laravel: 內建 @csrf Blade 指令 + VerifyCsrfToken 中間件,已自動實現 Token 驗證 + PRG。
  • Symfony: CsrfTokenManager + Form 組件自動處理。
  • ThinkPHP: token 驗證規則 + redirect() 實現 PRG。
  • 原生/微框架: 可封裝成 CsrfMiddleware 或 Trait 復用上述邏輯。

總結

「Session Token 去重 + PRG 重導向」是防止表單重複提交的標準解法,代碼量極小、零依賴、適用於所有 PHP 專案。請務必在每個「寫入型」表單中落地執行。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved