你首先要问自己的问题是:你想避免什么样的问题场景?
- 用户不小心(或出于沮丧,...)单击按钮两次。
- 只能使用一次的资源(例如飞机上某个座位的预订)被消耗两次。
不要只说“我想避免两者”。即使这样做,您也必须分别处理这两个问题。
问题 1
这可以在客户端更好地解决(例如,一旦单击按钮就禁用它)。
它也可以在服务器端解决(通过检查序列号或令牌或者可能是内容的哈希码,......),但我真的不明白这一点。如果用户真的想提交两次(例如通过操作 JavaScript 使按钮不会被禁用),那么就让他们:问题 1 与安全性无关。
问题 2
这必须在服务器端解决(除非在非常特殊的情况下)。这主要是关于安全性。但是仔细一想,这个问题并不能通过双重提交预防来解决!为什么不?
让我们看一下我们的示例:飞机上的座位只能预订一次。这可以通过多种方式违反:
- 通过双重提交。
- 由同一用户同时提交,例如从不同的浏览器窗口。
- 由多个用户尝试同时预订。
解决问题的干净方法是通过保留座位以原子方式检查座位的可用性。如果违规是由重复提交引起的,这并不重要(问题 1 涵盖了意外的重复提交)。
...和问题 3
如果你已经实现了一些自动重提交机制,那么你可能还会遇到第三种问题:
假设用户想要将商品添加到他的购物车。客户端提交,超时前未收到来自服务器的响应。所以它会自动再次发送。然而,服务器接收到这两条消息,并尝试同时处理它们——因此它将商品两次添加到购物车中。
在我看来,避免这种情况的最佳解决方案通常不是使用“将一件商品添加到购物车”之类的操作,而是“将商品的目标数量设置为 1”。同样,您也可以使用序列号等。