为什么需要防范重复提交呢? 举个最直接的栗子: 你在商城里买了 7888 元的 iphone x, 付款后页面卡顿导致你重复点击了付款按钮, 这时候如果后端不加重复交易验证的话, 相当于付款 15766 元买了 Iphone x 手机, 划算吧?
不单是互金系统交易时会生产此问题, 凡涉及表单提交都会遇到, 这里以某互金系统为例说明交易防重的过程设计. 下图是交易防重设计的示图:
这个过程相信大家都不陌生, 生活中随处可见. 开封菜的甜品站, 先付款, 再给小票, 拿着小票到取餐口拿甜品, 交易完成后, 小票撕毁. 这就是一个典型的防止重复取餐的例子.
回到上图, 来深入了解一下这个过程:
1, 在进入到需要防重交易的表单页面之前, 请求后端生成 token 的服务, 生成 token 并存储在后端, 与该用户的请求绑定, 便于后期在交易验证时与之比对, token 返回到交易页面.
2, 携带 token 提交表单, 在进入真正交易之前, 做 token 验证 (比如使用 AOP), 如果存在, 则 token 正常, 比对成功后销毁进入正常的交易功能. 如果不存在, 则证明 token 已经被销毁, 为重复提交请求.
以上步骤可以看出 token 的关键性, 若 token 获取失败, 那么交易将无法完成, 所以需要保证 token 服务的高可用性.
以上过程针对一个交易是完全没有问题的, 但若涉及两个以上的关键交易提交时, 就会出现后请求的交易获取的 token 替换首次交易获取的 token, 那么在首次交易提交时, 会出现 token 找不到的情况, 导致交易失败. 由此引出另外两个关键的问题点:
token 的数量以及 token 的销毁机制. 数量决定了能同时发起交易的数量, 所以 token 的数量最好能够覆盖所有关键交易同时发起来的数量. token 的销毁决定了使用 token 的正常顺序.
基于上面流程, 我们再改造一下生成 token 的模块.
关键示例代码:
生成 token 方法
交易校验, 主要由拦截器完成.
一般的解决方案是在前端由 JS 控制提交表单按钮, 提交后置灰, 禁止第二次提交. 但此方法也只针对小白用户有效, 防范机制也不是很彻底, 比如直接调用请求而非通过页面表单进行, 比如 JS 校验代码清除等, 可以绕过 JS 的置灰功能进行二次提交.
采用前端 JS 置灰防止重复提交请求, 再加上后端 token 验证, 可以更有效的防止关键交易的重复提交.
来源: http://www.tuicool.com/articles/3IjuYzF