表单重复提交请求:
由于重复点击或者网络重发
1)点击提交按钮两次;
2)点击刷新按钮;
3)使用浏览器后退按钮重复之前的操作,导致重复提交表单;
4)使用浏览器历史记录重复提交表单;
5)浏览器重复的HTTP请;
6)nginx重发等情况;
7)分布式RPC的try重发等;
作者:锦成同学
链接:https://juejin.im/post/5d31928c51882564c966a71c
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
创建一个 Dynamic Web Project 演示
FormServlet
index.jsp
访问 http://localhost:8080/form-repeat-demo/,因为请求的延迟,快速点击按钮,会导致重复提交。
1、前端表单提交后通过 js 禁止按钮点击
<form action="${pageContext.request.contextPath}/FormServlet" method="post">
<input type="text" name="name" /><br/>
<input type="button" value="提交" onclick="handleSubmit(this)" />
</form>
<script type="text/javascript">
function handleSubmit(buttonObj) {
document.forms[0].submit();
buttonObj.disabled = true;
}
</script>
点击按钮后,按钮变暗,不能继续点击;浏览器跳转到 http://localhost:8080/form-repeat-demo/FormServlet
但是 F5 刷新或者后退,仍然可以重复提交表单。所以,这种方法并不能完全解决。
2、token 令牌机制(表单令牌与服务器保存的令牌比对)
token 令牌机制:表单提交时发送一个 token,这个 token 之前已经保存在 服务器,当处理一次请求后删除 服务器中 token,保证只处理一次表单提交请求。
这里演示服务器令牌保存在 session。
修改 index.jsp:
FormServlet 中判断表单提交 的 token 和 session 中的 token
这样,点击提交按钮后,发送请求,浏览器地址变为提交表单的地址 http://localhost:8080/form-repeat-demo/FormServlet。F5 刷新页面和回退页面继续点击提交按钮,都会提示重复提交信息。
这样,表单就只能提交一次。如果想要继续提交,需要刷新页面,重写生成页面更新 token。
上面的演示,显然需要使用到动态页面技术(获取到的页面是动态页面,需要往 session 中添加 token)。
前后端分离架构中,前端都是静态页面,数据提交时要向服务申请 token(或前后端使用同一规则),token 放到redis 或 内存。提交后同时删除 token。
token 生成可以参考 本文最后链接 1 和 3。
3、nonce 机制 + 表单校验
nonce 机制:每一次请求过来 nonce 加一;后端保存前一次请求的 nonce,校验每次请求的 nonce。如果nonce 不大于保存的 nonce,则认为是重复请求。这其实也是防重放攻击的一种处理。HTTP 中摘要(Digest )认证就是采用这种机制。
另外为了保证每次请求都是用户希望的请求,表单输入可以做非空校验等,点击提交按钮后清空输入。这样用户重复点击,也通不过校验。
4、借助数据库
insert 使用唯一索引, update使用乐观锁 version版本法 这种在大数据量和高并发下效率依赖数据库硬件能力,可针对非核心业务
参考:
1)API接口幂等设计(Token方式防止表单重复提交或网络延迟)
2) 8种方案解决重复提交问题
6) https://www.jianshu.com/p/364a6f466a2a
7) https://cloud.tencent.com/developer/article/1460588
---