用户因网络延迟多次点击提交、浏览器刷新、恶意重放,导致同一业务逻辑重复执行(如重复下单、重复扣款)。需从客户端拦截、服务端幂等两个维度构建防御体系。
<input type="submit" value="提交" onclick="this.disabled=true; this.value='提交中...'; this.form.submit();">
<script>
let submitted = false;
function checkForm(form) {
if (submitted) {
alert('正在提交,请勿重复点击');
return false;
}
// 表单校验...
submitted = true;
return true;
}
</script>
<form onsubmit="return checkForm(this)">...</form>
<?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 则失效。
<?php
session_start();
$token = bin2hex(random_bytes(16)); // 加密安全随机数
$_SESSION['form_token'] = $token;
?>
<input type="hidden" name="form_token" value="<?= $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 绑定用户会话)。
处理完成后 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']。单一手段皆有漏洞,**“客户端拦截体验 + Session Token 幂等 + PRG 重定向 + DB 唯一键兜底”** 四层防御体系,可覆盖 99.9% 重复提交场景,是成熟 PHP 应用的标准做法。