You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:表单数据为何会被多次重复插入数据库?

解决表单重复插入数据库的问题

我来帮你排查并解决这个重复插入的问题,先从代码里的几个关键点分析:

可能的触发原因

1. 表单提交事件被重复绑定

你的JS代码是在$(document).ready()里直接绑定#create_account的submit事件,如果页面存在动态加载(比如你用load方法刷新表单区域),每次加载都会重新绑定一次事件,导致提交时触发多次请求,自然就会重复插入数据。

2. 未限制重复点击提交按钮

如果用户快速多次点击提交按钮,在第一次请求还没完成时就发起新请求,也会导致多条重复数据插入。

3. 后端缺乏重复提交防护

服务器端没有做唯一性校验(比如邮箱是否已存在),也没有防重复提交的机制,只要收到请求就执行插入,哪怕是重复请求。

另外,你的PHP代码还存在严重的SQL注入风险,这个也得顺便修复,不然安全隐患很大。


具体解决方案

方案一:修复前端JS,防止重复触发请求

把事件绑定改成事件委托,并且在提交后禁用按钮,避免重复点击:

$(document).ready(function(){
    // 用事件委托绑定,即使表单动态加载也只会绑定一次
    $(document).on("submit", "#create_account", function(event){
        event.preventDefault();
        const $form = $(this);
        const $submitBtn = $form.find("#create_accountSbm");
        
        // 提交后立刻禁用按钮,防止重复点击
        $submitBtn.prop("disabled", true).text("提交中...");

        // 直接序列化表单数据,比手动取值更简洁不易出错
        const formData = $form.serialize();

        $("#return_new_Account").load('/blog/form', formData, function(response, status){
            // 请求完成后恢复按钮状态(如果需要允许用户再次提交的话)
            $submitBtn.prop("disabled", false).text("创建账户");
            // 如果请求失败可以在这里提示用户
            if(status === "error") {
                $("#return_new_Account").text("提交失败,请重试");
            }
        });
    });
});

方案二:后端添加唯一性校验+修复SQL注入

1. 先检查邮箱是否已存在,避免重复插入

修改你的业务逻辑代码,在插入前先查询邮箱是否已注册:

if(empty($validate->message)) {
    $dbo = database::getInstance();
    // 先校验邮箱是否已存在
    $checkSql = "SELECT userEmail FROM " . $user->_table . " WHERE userEmail = ?";
    $dbo->doQuery($checkSql, [$_POST['email']]);
    
    if($dbo->_numRows === 0) {
        // 邮箱不存在才执行插入
        $data = array(
            'userFname' => $_POST['firstname'],
            'userLname' => $_POST['lastname'],
            'userEmail' => $_POST['email'],
            'userPsw' => $_POST['password'],
        );
        $user->insert($data);
    } else {
        $validate->message = "该邮箱已经注册过啦";
    }
}

2. 用预处理语句修复SQL注入问题

你的原代码直接把用户输入拼接到SQL里,非常容易被攻击,改成预处理语句:

// 修改doQuery方法,支持预处理参数
public function doQuery($sql, $params = []){
    $stmt = $this->_conn->prepare($sql);
    if(!empty($params)){
        // 根据参数类型生成占位符类型字符串,这里假设都是字符串,实际可以按需调整
        $types = str_repeat('s', count($params));
        $stmt->bind_param($types, ...$params);
    }
    $stmt->execute();
    $this->_reuslt = $stmt->get_result();
    $this->_numRows = $this->_reuslt->num_rows;
}

// 修改insert方法,使用预处理语句
function insert($data){
    $dbo = database::getInstance();
    $columns = implode(", ", array_keys($data));
    // 生成和参数数量对应的?占位符
    $placeholders = implode(", ", array_fill(0, count($data), '?'));
    $sql = "INSERT INTO " . $this->_table . " ({$columns}) VALUES ({$placeholders})";
    $dbo->doQuery($sql, array_values($data));
}

方案三:添加CSRF令牌,彻底防止重复提交和跨站请求

这个是进阶的防护措施,能有效防止恶意重复提交和跨站请求伪造:

  1. 在表单里添加隐藏的CSRF令牌:
<form id="create_account" method="post">
    <!-- 你的其他表单字段 -->
    <input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
    <button type="submit" id="create_accountSbm">创建账户</button>
</form>
  1. 在PHP页面开头生成并存储令牌:
session_start();
// 生成唯一的CSRF令牌
if(empty($_SESSION['csrf_token'])){
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
  1. 在业务逻辑里验证令牌:
session_start();
// 验证CSRF令牌是否有效
if(empty($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']){
    die("无效的请求,请刷新页面重试");
}
// 验证后销毁令牌,防止重复使用
unset($_SESSION['csrf_token']);

// 后续的表单验证和插入逻辑...

按照上面的步骤调整后,既能解决重复插入的问题,又能修复安全漏洞,应该就能正常工作了。

内容的提问来源于stack exchange,提问作者Oliver Larsen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:18:26