26

我想看看 Web 开发人员如何避免重复提交问题。所以基本上我对这个问题的理解如下:

当不耐烦的用户多次提交表单时,会发生双重提交,从而导致问题。这个问题可以通过 JavaScript(特别是 jQuery 脚本)来解决,一旦表单提交就禁用提交按钮 - 如果客户端禁用了 JavaScript,则此问题的一个弱点是。

还有服务器端的检测方法。

所以我的问题是:

人们如何克服双重屈服?双重提交导致问题的真实例子是什么?是否有任何 Web 应用程序框架内置了双重提交工具?

4

7 回答 7

20

现实生活中的情况:在投注网站上投注。用户将双击并获得两个赌注。不好!Javascript 检查不足以防止这种情况。

解决方案:

  1. 使用呈现表单的服务器端脚本语言在表单中创建UUID / GUID隐藏输入。

  2. 在提交表单时,立即将其添加到名为UniqueSubmissions(例如)的数据库表中。然后继续处理。

  3. 如果在UniqueSubmissions表中找到具有相同 UUID/GUID 的每个后续请求,将被拒绝。

这对我们有用。希望能帮助回答你的问题!

于 2011-01-16T12:10:38.883 回答
8

如果您正在使用 java 服务器端脚本并且还使用 struts 2,那么您可以参考这个关于使用 token 的链接。

http://www.xinotes.org/notes/note/369/

应为初始页面渲染生成一个令牌并将其保存在会话中,当请求与令牌第一次一起提交时,在 struts 操作中运行一个线程名称作为令牌 id 的线程并运行客户端拥有的任何逻辑请求,当客户端再次提交相同的请求时,检查线程是否仍在运行(thread.getcurrentthread().interrupted)如果仍在运行,则发送客户端重定向 503。

请看struts 2code的ExecuteAndWaitInterceptor,这个逻辑结合token有助于快速点击

于 2011-01-15T12:40:06.110 回答
7

使用redirect-after-post或有时称为PRG (post/redirect/get)

简而言之,当用户发布表单时,您执行客户端重定向(在使用发布数据之后)到响应(成功)页面。

于 2011-01-15T12:30:41.290 回答
5

一个真实的例子是这个答案发布了两次;-)。如果您不想依赖客户端的任何方面(javascript,甚至 cookie),您可以计算提交数据的 MD5 哈希,可能通过添加源 IP 和使用的浏览器等信息,并拒绝帖子具有相同的哈希值。

于 2011-01-15T13:03:51.007 回答
3

web2py框架具有针对双重表单提交的内置保护。它将一次性令牌存储在会话中以及表单的隐藏字段中,它们必须在提交时匹配,否则提交被拒绝。此方法还可以防止 CSRF(跨站点请求伪造)。

于 2011-02-06T02:47:55.863 回答
2

如果表单打算为在服务器 dbms 中保存一些数据提供接口,您可以使用对于提交的数据来说是必需的特殊修订字段。检查提交的修订是否与数据库中数据的最新版本匹配(或者是要插入的新数据),可以让您很好地控制在多次提交时应该做什么按顺序。

于 2011-01-15T12:37:04.423 回答
1

使用 struts web-application framework 我们可以处理这个问题如下:

Struts 有 3 种方法用于token, saveToken(), isTokenValid() and resetToken().

saveToken()- 生成令牌密钥并保存到请求/会话属性。
isTokenValid()- 针对请求/会话中的 1 个存储验证提交的令牌密钥。
resetToken()- 重置令牌密钥。

它是如何工作的:
1) 在加载表单时,调用saveToken()动作类来创建和存储令牌键。Struts 会将生成的密钥存储在请求/会话中。如果令牌成功创建,在浏览器上查看源代码时会看到类似以下内容,令牌密钥存储为隐藏字段:

<form action="myaction.do" method="post"> 
 <input type="hidden" 
 name="<%= Constants.TOKEN_KEY %>" 
 value="<%= session.getAttribute(Action.TRANSACTION_TOKEN_KEY) %>" > 

2) 一旦表单提交,isTokenValid()在动作类上调用,它将使用先前存储在请求/会话中的令牌密钥来验证提交的令牌密钥(隐藏字段)。如果匹配,它将返回 true。

public final ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
    saveToken(request);
    if (!tokenIsValid(request)) {
        //forward to error page saying "your transaction is already being processed" 
    } else {
        //process action 
        //forward to jsp 
    }
    // Reset token after transaction success. 
    resetToken(request);
}

参考

于 2015-08-13T11:37:24.183 回答