本文目录导读:

在PHP项目中防止表单重复提交是一个常见的需求,以下是几种常用且有效的解决方案:
Token验证(最推荐)
实现原理
在表单中嵌入一个唯一Token,提交时验证Token是否已被使用。
代码实现
<?php
session_start();
// 生成Token
function generateToken() {
$token = bin2hex(random_bytes(32));
$_SESSION['form_token'] = $token;
return $token;
}
// 验证Token
function verifyToken($token) {
if (!isset($_SESSION['form_token']) || $_SESSION['form_token'] !== $token) {
return false;
}
unset($_SESSION['form_token']); // 用完立即销毁
return true;
}
?>
<!-- HTML表单 -->
<form method="POST" action="process.php">
<input type="hidden" name="token" value="<?php echo generateToken(); ?>">
<!-- 其他表单字段 -->
<button type="submit">提交</button>
</form>
处理提交:
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!isset($_POST['token']) || !verifyToken($_POST['token'])) {
die('表单已提交,请勿重复操作');
}
// 处理表单数据...
}
时间戳防重
实现原理
记录提交时间,限制多次提交的时间间隔。
<?php
session_start();
// 设置提交间隔(秒)
$submit_interval = 3;
function checkSubmitTime() {
global $submit_interval;
$current_time = time();
if (isset($_SESSION['last_submit_time'])) {
$time_diff = $current_time - $_SESSION['last_submit_time'];
if ($time_diff < $submit_interval) {
return false;
}
}
$_SESSION['last_submit_time'] = $current_time;
return true;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!checkSubmitTime()) {
die('请勿频繁提交,请等待' . $submit_interval . '秒');
}
// 处理表单...
}
数据库唯一约束
实现原理
在数据库层面使用唯一索引,防止重复插入。
-- 创建表时添加唯一约束
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(50) UNIQUE,
-- 其他字段...
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 或在已有表上添加
ALTER TABLE orders ADD UNIQUE INDEX idx_order_no (order_no);
PHP代码:
<?php
$order_no = 'ORD_' . time() . '_' . uniqid();
try {
$stmt = $pdo->prepare("INSERT INTO orders (order_no, ...) VALUES (?, ...)");
$stmt->execute([$order_no, ...]);
} catch (PDOException $e) {
if ($e->getCode() == 23000) { // 唯一约束违反
die('该订单已提交,请勿重复操作');
}
throw $e;
}
JavaScript前端限制
实现原理
提交后立即禁用提交按钮。
<form id="myForm" method="POST" action="process.php" onsubmit="return handleSubmit()">
<!-- 表单字段 -->
<button type="submit" id="submitBtn">提交</button>
</form>
<script>
function handleSubmit() {
var btn = document.getElementById('submitBtn');
btn.disabled = true; // 禁用按钮
btn.textContent = '提交中...';
// 或者阻止多次点击
// btn.onclick = function() { return false; }
return true;
}
</script>
综合方案(推荐)
组合使用多种方法,达到最佳效果:
<?php
session_start();
class AntiDuplicateSubmit {
private $interval = 3; // 秒
public function generateToken() {
$token = bin2hex(random_bytes(32));
$_SESSION['form_token'] = $token;
$_SESSION['token_time'] = time();
return $token;
}
public function validate($token) {
// 1. Token验证
if (!isset($_SESSION['form_token']) || $_SESSION['form_token'] !== $token) {
return false;
}
// 2. 时间间隔验证
$time_diff = time() - $_SESSION['token_time'];
if ($time_diff < $this->interval) {
return false;
}
// 3. 清除Token,防止重复使用
unset($_SESSION['form_token']);
unset($_SESSION['token_time']);
return true;
}
}
// 使用
$antiDuplicate = new AntiDuplicateSubmit();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['token'] ?? '';
if (!$antiDuplicate->validate($token)) {
// 记录日志(可选)
error_log("Detected duplicate submission from IP: " . $_SERVER['REMOTE_ADDR']);
die('提交失败:请勿重复提交或等待3秒后重试');
}
// 处理表单...
}
?>
注意事项
- 永远不要只依赖前端验证,必须配合后端验证
- Token必须足够随机,使用
random_bytes()而不是rand() - 及时销毁Token,验证成功后立即删除
- 考虑用户体验,给出友好的错误提示
- 记录日志,便于排查问题和防止恶意攻击
选择哪种方法取决于你的具体需求,对于大多数场景,Token验证 + 前端按钮禁用 的组合已经足够安全。