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

PHP 防止表单重复提交的五大策略:客户端与服务端双重防护完整指南

     編輯:PHP 綜合知識

问题本质

用户因网络延迟多次点击提交、浏览器刷新、恶意重放,导致同一业务逻辑重复执行(如重复下单、重复扣款)。需从客户端拦截、服务端幂等两个维度构建防御体系。

策略一:客户端 JS 拦截(首道防线)

1.1 提交按钮置灰法

<input type="submit" value="提交" onclick="this.disabled=true; this.value='提交中...'; this.form.submit();">

1.2 标志变量法(推荐)

<script>
let submitted = false;
function checkForm(form) {
    if (submitted) {
        alert('正在提交,请勿重复点击');
        return false;
    }
    // 表单校验...
    submitted = true;
    return true;
}
</script>
<form onsubmit="return checkForm(this)">...</form>

策略二:Cookie 标记法(简单但依赖客户端)

<?php
if (isset($_POST['submit'])) {
    if (isset($_COOKIE['form_submitted'])) {
        die('请勿重复提交');
    }
    setcookie('form_submitted', '1', time() + 30); // 30 秒有效期
    // 业务处理...
    header('Location: success.php'); // PRG 模式
    exit;
}
?>

⚠️ 用户禁用 Cookie 则失效。

策略三:Session Token 法(核心服务端方案)

3.1 生成 Token 存入 Session 与表单隐藏域

<?php
session_start();
$token = bin2hex(random_bytes(16)); // 加密安全随机数
$_SESSION['form_token'] = $token;
?>
<input type="hidden" name="form_token" value="<?= $token ?>">

3.2 处理页验证并消费 Token

<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $submittedToken = $_POST['form_token'] ?? '';
    $sessionToken   = $_SESSION['form_token'] ?? '';

    if (!$submittedToken || $submittedToken !== $sessionToken) {
        die('非法请求或重复提交');
    }

    // 验证通过,立即清除/更新 Token,防止二次提交
    unset($_SESSION['form_token']);
    $_SESSION['form_token'] = bin2hex(random_bytes(16));

    // 业务逻辑...
    echo '提交成功';
}
?>

✅ 可防刷新、防重放、防 CSRF(若 Token 绑定用户会话)。

策略四:PRG 模式(Post-Redirect-Get)

处理完成后 302 重定向 到结果页,浏览器地址栏变为 GET 请求,刷新不再重复 POST。

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 业务处理...
    header('Location: /order/success?id=123', true, 302);
    exit;
}
?>

✅ 最简单有效,配合 Token 双重保险。

策略五:数据库层面幂等键(终极兜底)

业务表增加 unique_key (user_id, business_type, request_sn) 唯一索引,request_sn 由前端生成(UUID/雪花算法)随表单提交。数据库插入冲突即为重复。

INSERT INTO orders (user_id, amount, request_sn) 
VALUES (1001, 99.00, 'req_abc123...')
ON DUPLICATE KEY UPDATE updated_at = NOW();

表单回填与缓存控制(补充)

  • 防止回退丢数据:header('Cache-Control: private, must-revalidate'); 或 session_cache_limiter('private'); (需在 session_start() 前)。
  • 区分多个提交按钮:同名不同值 <input name="action" type="submit" value="保存">,后端判断 $_POST['action']。

综合推荐架构

  1. 前端:JS 禁用按钮 + 标志变量拦截。
  2. 网关/中间件:可选的请求去重(如 Redis 记录 request_sn 30 秒)。
  3. Controller:Session Token 验证 + 消费。
  4. Service/DB:业务唯一键约束兜底。
  5. 响应:PRG 重定向到结果页。

总结

单一手段皆有漏洞,**“客户端拦截体验 + Session Token 幂等 + PRG 重定向 + DB 唯一键兜底”** 四层防御体系,可覆盖 99.9% 重复提交场景,是成熟 PHP 应用的标准做法。

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