服务端防止重复提交 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
wleexi
V2EX    程序员

服务端防止重复提交

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

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

    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
    TagMonk  
       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  
    PRO
       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 一下,缓存个几秒(这个相当于加锁)。。。
        37
    Raymon111111  
       2018-11-26 11:46:38 +08:00
    前端让按钮只能点一下是最好的

    后端防恶意请求可以用 锁+数据库查询 的方法搞定幂等
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5125 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 09:10 PVG 17:10 LAX 01:10 JFK 04:10
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86