PHP前端表单提交时,如何避免重复数据我用了令牌

阿乐
阿乐 管理员 年卡会员
发布于 2026-09-04 08:44 ·18 浏览 ·0 回复

重复提交的烦恼

相信不少做过表单开发的朋友都遇到过这种场景:用户填了半天表单,点击提交后因为网络卡顿或误操作,又点了几下按钮,结果后端收到了好几条一模一样的数据。轻则多几条垃圾记录,重则造成订单重复、库存错乱。我自己就曾在项目里栽过跟头,后来采用“令牌”方式彻底解决了这个痛点。今天就来聊聊怎么用简单的令牌机制,在前端表单提交时挡住重复数据。

令牌机制的原理

令牌(Token)防重复的核心思路是:**每次渲染表单时,后端生成一个一次性随机值,存到 Session 中,同时放进表单的隐藏字段**。用户提交时,后端拿表单里的令牌和 Session 中保存的令牌对比,如果不匹配,拒绝处理;如果匹配,则处理业务并立刻销毁这个令牌。这样,同一个令牌只能使用一次,第二次点击提交时令牌已经失效,自然就拦下了重复请求。

顺便说一句,这个机制也常用来防范 CSRF 攻击,一举两得。但今天重点还是防重复提交。

代码实现:从表单到后端

我用 PHP 举个例子,过程不复杂。

1. 生成令牌并写入 Session

在展示表单的页面文件里:

session_start();
$token = bin2hex(random_bytes(16)); // 也可以用 uniqid() 或 md5()
$_SESSION['form_token'] = $token;

2. 把令牌放进表单

在 HTML 表单中添加一个隐藏字段:

<form method="post" action="submit.php">
    <!-- 其他字段 -->
    <input type="hidden" name="token" value="<?php echo htmlspecialchars($token); ?>">
    <button type="submit">提交</button>
</form>

3. 后端校验令牌

在提交处理脚本 submit.php 中:

session_start();

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 检查令牌是否存在且匹配
    if (!isset($_POST['token']) || $_POST['token'] !== $_SESSION['form_token']) {
        // 令牌错误或已失效,可能是重复提交
        die('提交无效,请刷新页面后重试');
    }

    // 验证通过,立即销毁令牌,确保只能使用一次
    unset($_SESSION['form_token']);

    // 然后进行业务处理:保存数据、发邮件等
    // ...
    
    echo '提交成功';
}

关键点在于 `unset` 的时机。必须在业务处理之前销毁,否则并发请求仍可能通过校验。同时,如果业务处理失败需要让用户重试,可以重新生成一个令牌放回 Session,并刷新页面。

其它值得注意的细节

令牌过期:用户打开页面很久才提交,Session 过期可能导致令牌丢失。建议设置合理的有效期,或者在令牌校验失败时给出友好的提示,让用户刷新页面后重新填写,而不是简单报错。

多标签页:如果用户开了两个表单页,后打开的表单会覆盖之前的令牌,导致前一个表单提交失败。一种解决方法是给每个表单分配独立令牌 ID,或者接受这种“强制刷新”行为,在业务上引导用户刷新页面。

前后端联动:除了令牌机制,前端也可以做按钮防抖、提交后禁用按钮,甚至用 JS 标记已提交状态。但注意,前端限制只是改善体验,真正可靠的后端校验还是得靠令牌这类方案。

总结

用令牌防重复提交,成本很低、效果明显,而且顺手提升了安全水位。核心就是“一表单一令牌,用完即弃”。下次再有人跟我抱怨表单被疯狂点击,我直接甩这段代码过去,顺便说一句:令牌在手,重复没有。希望这个小技巧也能帮你省下几杯咖啡的时间。

他们都看过 3 人浏览过
阿乐断了的弦一只冷漠的狐狸

全部回复 0

还没有回复,来抢沙发~