【问题标题】:Are there techniques to prevent double submissions in stateless web applications?是否有技术可以防止无状态 Web 应用程序中的重复提交?
【发布时间】:2011-11-20 09:32:28
【问题描述】:

我想在现有的 java web 应用程序(实际上是 struts)中实现双重提交预防。架构方面,我们谈论的是 2 到 N 个可能的应用程序服务器(tomcat)和一个单一的数据库服务器(mysql)。各个服务器彼此不认识,无法交换消息。在应用服务器前面有一个负载均衡器,它能够执行粘性会话

所以基本上有两种防止双重提交的客户端和服务器端。如果可能的话,我想去服务器端,因为如果人们在浏览器中禁用 cookie 和/或 javascript,所有客户端技术似乎都会失败。

这让我想到了通过数据库锁进行某种类似互斥锁的同步。我认为可以计算用户输入数据的校验和并将其保存到专用的数据库表中。在每次提交时,应用程序必须检查是否存在相等的校验和,这表明给定的提交是重复的。当然,必须定期清除此表中的校验和。问题是检查数据库中是否已经存在重复校验和并在没有校验和时插入校验和的整个过程几乎是一个关键部分。因此校验和表必须预先锁定并在节后再次解锁。

当我想到桌子锁时,我的死锁瓶颈警钟开始响起。所以我的问题是:有没有更明智的方法来防止无状态 Web 应用程序中的重复提交?

请注意,struts TokenInterceptor 不能在这里应用,因为当 cookie 被禁用时它会惨遭失败(它依赖于 HTTP 会话,如果没有会话 cookie,它根本不存在)。

【问题讨论】:

  • 你知道POST-REDIRECT-GET pattern吗?对你的情况没有用吗?
  • 防止无状态双重提交不是不可能吗?不是双重提交令牌状态,存储在哪里?一个相同的事务可能不会被双重提交,它可能只是一个相同的事务。所以检查你的数据层可能也行不通。
  • 您是否预计会有很多没有 JavaScript 没有 cookie 的用户?您确定投资回报率值得付出努力吗?在任何情况下,您几乎都将自己置于依赖数据库哈希的角落,因为您不能保证第二次点击将由同一台服务器处理,也不能保证应用程序状态会足够快地复制。您还需要非常快地使这些哈希条目过期,因为这可能是合法的重新提交。也许保留一个大的内存映射,而不是使用几个实现(或内存数据库)中的任何一个?
  • @brandizzi 这种模式很有用,但在我的情况下服务器对提交的回复可能需要一些时间并且用户通常会再次提交相同的请求。
  • @DaveNewton 我不知道为什么,但似乎有很多用户没有启用 cookie 和/或 JavaScript。由于这个原因,之前依赖 HTTP 会话属性的简单解决方案确实失败了。

标签: java web-applications struts stateless double-submit-prevention


【解决方案1】:

一个更简单的基于数据库的解决方案是这样的。这也可以在多种表单中通用。

  • 拥有可用于存储令牌的数据库表。
  • 显示新表单时 - 在令牌表中插入新行 并将令牌添加为表单中的隐藏字段。
  • 当您收到表单提交时,请在行上执行 select for update 对应于您在表单中收到的令牌。
  • 如果该行仍然存在,那么这是第一次提交。处理 提交并删除该行。
  • 如果该行不存在,则该表单已被处理 - 你可以返回一个错误。

【讨论】:

  • 我会尝试一下这个解决方案,因为您建议使用行级锁而不是表锁。这对我来说似乎比我的方法更有意义。
【解决方案2】:

防止重复提交的经典技术是分配两个 ID(都作为 HTML 表单标签中的“隐藏”字段) - 一个从登录到注销保持相同的“会话 ID”...

每次提交时第二个 ID 都会更改...在服务器端,您只需要跟踪“当前有效 ID”(特定于会话)...如果您收到“重新提交”(通过单击 - happy-user 或“刷新按钮”或“后退按钮”或...)那么这将与当前 ID 不匹配...这样您就知道:应该丢弃此提交并生成新 ID并带回了答案。 一些实现使用一个在每次提交时都会修改的 ID,这可以稍微简化检查/kepp 跟踪部分,但可能容易受到“猜测”(安全问题)的影响...... 我喜欢为这种保护生成加密强 ID...

如果您有一个具有粘性会话的负载平衡环境,那么您只需要跟踪服务器本身(内存中)上的 ID...但您当然可以将 ID 存储在数据库中...因为您将它与会话 ID 一起存储,锁定将位于“行级别”(不是表级别),这应该没问题。

您描述的方式通过检查内容更进一步......但是我在“应用程序逻辑”级别上看到的内容部分比“重新提交防止级别”更多,因为它取决于应用程序逻辑是否它想再次接受相同的数据......

【讨论】:

  • 如果没有会话,则没有帮助,这是 OP 的假设。
  • OP 发布的是“使用粘性会话进行负载平衡”,这反过来意味着存在某种会话,因此上述机制有效......
【解决方案3】:

如果您使用粘性会话,那么您可以使用一些 TokenManagement。存在一个DoubleClickFilter,您可以将其添加到您的web.xml

由于您有粘性会话,因此不需要跨 Tomcat 解决方案。

【讨论】:

    猜你喜欢
    • 2011-08-08
    • 2017-12-16
    • 2020-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 2021-01-20
    • 2017-03-07
    相关资源
    最近更新 更多