V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
wleexi
V2EX  ›  程序员

服务端防止重复提交

  •  
  •   wleexi · 2018-11-25 10:21:52 +08:00 · 7409 次点击
    这是一个创建于 1950 天前的主题,其中的信息可能已经有所发展或是发生改变。

    某个请求对应的是一个数据库的插入操作。从服务端角度来看,如何防止因为页面连续点击造成的重复提交。

    37 条回复    2018-11-26 11:46:38 +08:00
    leriou
        1
    leriou  
       2018-11-25 10:35:32 +08:00   ❤️ 1
    csrf , 或者自己用事件 id 去重
    utoyuri
        2
    utoyuri  
       2018-11-25 10:35:41 +08:00   ❤️ 5
    根据 request 内容生成一个 unique key 塞到 redis,结束后删掉。接到新 request 后在 redis 里面找一下,存在就丢弃这个 request。
    lihongjie0209
        3
    lihongjie0209  
       2018-11-25 10:37:58 +08:00
    @leriou 跨站请求伪造 和 服务端有什么关系?
    t6attack
        4
    t6attack  
       2018-11-25 10:41:19 +08:00
    参考眼前的页面。
    <input type="hidden" value="xxxxx" name="once">
    wleexi
        5
    wleexi  
    OP
       2018-11-25 10:47:44 +08:00
    @t6attack
    @leriou

    前端保持不变的情况下,单靠服务端来处理有什么办法么。
    wleexi
        6
    wleexi  
    OP
       2018-11-25 10:54:18 +08:00
    @utoyuri 类似生成 md5 这种么。
    Sharuru
        7
    Sharuru  
       2018-11-25 11:02:40 +08:00 via Android
    PRG
    allanzhuo
        8
    allanzhuo  
       2018-11-25 11:08:38 +08:00 via Android
    token
    jugelizi
        9
    jugelizi  
       2018-11-25 11:08:39 +08:00
    就是表单 token 啊 如果不是前后端分离的话
    leriou
        10
    leriou  
       2018-11-25 11:11:30 +08:00   ❤️ 1
    @wleexi 其实就是你需要能识别出来相同的请求, 做幂等处理, csrf 的原理是你每次前端的表单页面生成时候提供一个唯一的 csrf_id, 发起请求的时候这个 id 会跟着一起到达后端,标记本次请求, 如果后端发现这个 id 已经处理过一次了, 就说这次请求就是没有刷新页面情况下的重复的提交
    wleexi
        11
    wleexi  
    OP
       2018-11-25 11:15:40 +08:00
    @jugelizi
    @allanzhuo

    有考虑过提交完成后把信息塞到 cookie 里,下一次提交如果 cookie 里发现有就不做插入。
    如果提交是异步,前一次请求每完成,接着提交了第二次。。。也不能解决这个问题
    godoway
        12
    godoway  
       2018-11-25 11:19:26 +08:00 via Android
    @jugelizi 那么如果前后端分离怎么防止重复提交。
    lihongjie0209
        13
    lihongjie0209  
       2018-11-25 11:21:14 +08:00
    @leriou 前端保持不变的情况下 这个办法不可行, 你要改表单的逻辑
    allanzhuo
        14
    allanzhuo  
       2018-11-25 11:25:16 +08:00 via Android
    @wleexi 我之前瞎写的,你可以参考下 https://www.cnblogs.com/laoyeye/p/9557269.html
    allanzhuo
        15
    allanzhuo  
       2018-11-25 11:26:53 +08:00 via Android
    @allanzhuo 之前瞎写的一点思考,可以参考下
    wleexi
        16
    wleexi  
    OP
       2018-11-25 11:28:24 +08:00
    @leriou 就是说还需要搞个地方存这个 id 对么。
    allanzhuo
        17
    allanzhuo  
       2018-11-25 11:30:42 +08:00 via Android
    @wleexi 提交表单前先请求 token,提交的时候吧 token 带着,当个参数。
    wleexi
        18
    wleexi  
    OP
       2018-11-25 11:31:54 +08:00
    @allanzhuo 意思懂,这样前端也要参与到改造中了,文章我看下,thx。
    wleexi
        19
    wleexi  
    OP
       2018-11-25 11:34:38 +08:00
    @allanzhuo 如果塞到 cookie 里不就好了 ,就是刚才说的异步那个问题,
    iyangyuan
        20
    iyangyuan  
       2018-11-25 12:53:12 +08:00 via iPhone
    直接把 token 放 cookie 里不就行了?
    checgg
        21
    checgg  
       2018-11-25 13:06:28 +08:00   ❤️ 1
    这个问题的实际需求是什么?
    1 防止提交两次?
    这只能客户端去解决。

    2 防止数据入库两次?
    那么重复提交的定义是什么?
    两个客户端,提交同一份数据,算重复提交吗?
    如果算,设置数据库唯一索引就好,写入相同数据会失败。
    如果不算,服务端没法验证。因为客户端请求都可以伪造。
    dagger
        22
    dagger  
       2018-11-25 13:42:08 +08:00
    你都说了是为了防止页面连续点击,那不去改前端的话,后端真的只能靠意念来感知了,就像前阵子那个要感知手机壳颜色一样
    utoyuri
        23
    utoyuri  
       2018-11-25 13:55:11 +08:00
    @wleexi 可以的 甚至明文都可以 只要唯一
    reself
        24
    reself  
       2018-11-25 14:07:48 +08:00 via Android
    @wleexi 可以把 csrf ——这个场景也可以称为 token ——放到 header 里,就不用改表单了
    TangMonk
        25
    TangMonk  
       2018-11-25 14:10:50 +08:00 via Android
    @lihongjie0209

    跨站请求伪造的 token 需要服务端来验证
    reself
        26
    reself  
       2018-11-25 14:11:23 +08:00 via Android
    @lihongjie0209 本质是 token,只是顺带具有 csrf 的功能,表单只是实现方式,放表单,放 session,放 header,放 meta 都可以
    TangMonk
        27
    TangMonk  
       2018-11-25 14:11:54 +08:00 via Android
    可以用类似 csrf 的 token,并且在表单在提交了后 disable 掉
    TangMonk
        28
    TangMonk  
       2018-11-25 14:12:32 +08:00 via Android
    可以用类似 csrf 的 token,并且在表单在提交了后把体检按钮 disable 掉
    TangMonk
        29
    TangMonk  
       2018-11-25 14:13:09 +08:00 via Android
    体检按钮☞提交按钮
    Vegetable
        30
    Vegetable  
       2018-11-25 14:22:00 +08:00 via Android
    总之就是需要一个事务 id。csrf 的 token 功能和这个需求有一定的交叉,可以使用但是不完全匹配。
    重复提交你说是连续点击,那么做个限速就好了,后几秒的请求丢弃就完了,用到缓存或者 session。这种需求前端配合禁用提交按钮好一点。
    paicha
        31
    paicha  
       2018-11-25 14:33:33 +08:00
    这是业务问题。
    showecho
        32
    showecho  
       2018-11-25 19:38:17 +08:00
    通常我是改前端,点击之后马上变为 disabled,这样就无法再次提交了,后端通常也有判断的逻辑,比如评论的话看时间间隔,注册的话比如邮箱唯一等;

    这应该就可以解决了吧?
    akira
        33
    akira  
       2018-11-25 22:53:22 +08:00
    @utoyuri 做个 3s 自动过期就好了,逻辑还简单点
    utoyuri
        34
    utoyuri  
       2018-11-26 01:27:14 +08:00
    @akira 很多个同样请求同时过来的时候 3s 过期时间就歇菜了
    utoyuri
        35
    utoyuri  
       2018-11-26 01:53:25 +08:00
    @akira 忽略我上一条弱智回复
    yc8332
        36
    yc8332  
       2018-11-26 09:16:03 +08:00
    表单 token 或者是 session 控制,还有同一个用户的提交数据 sha1 一下,缓存个几秒(这个相当于加锁)。。。
    Raymon111111
        37
    Raymon111111  
       2018-11-26 11:46:38 +08:00
    前端让按钮只能点一下是最好的

    后端防恶意请求可以用 锁+数据库查询 的方法搞定幂等
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3274 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 61ms · UTC 13:51 · PVG 21:51 · LAX 06:51 · JFK 09:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.