我想看看Web开发人员如何避免双重提交问题。基本上我对这个问题的理解如下:
当不耐烦的用户多次提交表单时会发生双重提交,从而导致出现问题。这个问题可以通过JavaScript(特别是jQuery脚本)来修复,这些脚本在提交表单后禁用了提交按钮 - 这样做的一个弱点就是客户端禁用了JavaScript。
还有服务器端检测方法。
所以我的问题是:
人们如何克服双重提交? 什么是双重提交引起的问题的真实例子? 任何Web应用程序框架都内置了双重提交工具吗?
答案 0 :(得分:16)
现实生活情况:在博彩网站上下注。用户双击并获得两次下注。不好! Javascript检查不足以阻止这种情况。
解决方案:
答案 1 :(得分:8)
如果您正在使用java服务器端脚本并使用struts 2,那么请参考此链接,该链接讨论使用令牌。
http://www.xinotes.org/notes/note/369/
当请求与令牌第一次一起提交时,应该生成令牌并保存在初始页面呈现的会话中,在struts动作中运行带有线程名称作为令牌ID的线程并运行逻辑客户端请求,当客户端再次提交相同的请求时,检查线程是否仍在运行(thread.getcurrentthread()。中断)如果仍在运行,则发送客户端重定向503。
请查看struts 2code的ExecuteAndWaitInterceptor,这个结合token的逻辑将有助于快速点击
答案 2 :(得分:7)
使用redirect-after-post或有时称为PRG (post/redirect/get)
简而言之,当用户发布表单时,您执行客户端重定向(在使用发布数据后)到响应(成功)页面。
答案 3 :(得分:5)
一个真实的例子就是这个答案发布两次;-)。 如果您不想依赖客户端的任何方面(javascript,甚至cookie),您可以计算提交数据的MD5哈希值,可能通过添加源IP和使用的浏览器等信息,并拒绝帖子具有相同的哈希值。
答案 4 :(得分:3)
web2py框架针对双重表单提交built-in protection。它在会话中以及表单中的隐藏字段中存储一次性令牌,并且它们必须在提交时匹配或拒绝提交。此方法也protects against CSRF(跨站点请求伪造)。
答案 5 :(得分:2)
如果表单的目的是提供用于在服务器dbms中保存某些数据的接口,则可以使用对提交的数据必需的特殊修订字段。检查提交的修订是否与数据库中数据的最新版本(或者是要插入的新数据的版本)相匹配,可以很好地控制如果提交多个提交内容该怎么做按顺序。
答案 6 :(得分:1)
使用struts web-application框架我们可以按如下方式处理这个问题:
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);
}