在Web开发中表单的重复提交是很严重的问题,重复提交成功会产生垃圾数据消耗不必要的资源,更严重的是如果遇到恶意刷库的情况垃圾数据更是数不胜数。在正常使用过程中产生重复提交的情况也有多重情况:鼠标连击、回退提交、刷新提交、网络延迟用户重复提交等。

防止重复提交的方法分两大类就是客户端、服务端(这是废话了)。客户端主要是用js对按钮的限制,一次点击后屏蔽按钮或者是直接跳转等待页面,服务端思路为客户端加token进行验证。客户端就不做详细介绍,主要介绍服务端的控制。

1、客户端存储

就是在客户端不同的地方存储两个token,在服务端进行校验。在Form表单中存储一个token利用隐藏域,在Cookie中存储一个(也可以都放到form表单中两个不同的隐藏域)。档form表单提交的时候,对这两个token进行验证,相同则允许提交否则阻止提交。

优点:

不占用服务器资源

实施起来简单,易上手

缺点:

容易伪造(防君子不防小人)

占用网络资源(或许不是那么明显)

详细介绍一下客户端分布存储在Form表单中和Cookie中的情况。

客户端的实现如下:

packagecn.simple.token;importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.apache.commons.lang.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/*** 双客户端验证

*@authorldm

* @Date 2016年6月16日*/@Service("clientTokenProcesser")public class ClientTokenProcesser extendsTokenProcesser {

@Autowired

HttpServletResponse response;

@Overridepublic booleanvalidToken(HttpServletRequest request) {

String formToken=request.getParameter(getTokenField()).toString();

System.out.println("formToken:"+formToken);if(StringUtils.isEmpty(formToken))

{

printException("表单中没有token");return false;

}

Cookie[] cookies=request.getCookies();if(cookies==null)

{

printException("cookie 中没有token");

}for(Cookie cookie : cookies) {if(cookie.getName().equals(getTokenKey(request)))

{

String cookieValue=cookie.getValue();

System.out.println("cookieToken:"+cookieValue);if(cookieValue.equals(formToken))

{return true;

}

}

}return false;

}private voidprintException(String msg) {

Exception e= newRuntimeException(msg);

e.printStackTrace();

}

@OverridepublicString getTokenKey(HttpServletRequest request) {

String cookieKey= getTokenField() + "_cookie";returncookieKey;

}

@Overridepublic voidsaveToken(HttpServletRequest request) {

String token=MakeToken.getInstance().getToken();

request.setAttribute(getTokenField(), token);if (response == null) {throw new RuntimeException("HttpServletResponse is null");

}

Cookie cookie= newCookie(getTokenKey(request), token);

response.addCookie(cookie);

}

@OverridepublicString getClientToken(HttpServletRequest request) {

Object token=request.getParameter(getTokenField());if (token == null) {return null;

}else{returntoken.toString();

}

}

}

View Code

2、双向存储

客户端和服务端的token各自独立存储,客户端存储在Cookie或者Form的隐藏域(放在Form隐藏域中的时候,需要每个表单)中,服务端存储在Session(单机系统中可以使用)或者其他缓存系统(分布式系统可以使用)中。

优点:

安全性高(几乎是无法伪造的)

网络资源相对于前者有所减少

缺点:

整个系统实施起来较第一种方法的时候复杂度增加

详细介绍一下服务端存储在session中客户端存储在Cookie中

SessionTokenProcesser实现如下:

packagecn.simple.token;importjavax.servlet.http.Cookie;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjavax.servlet.http.HttpSession;importorg.apache.commons.lang.StringUtils;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/*** 服务端用Session存储

*

*@authorldm

* @Date 2016年6月16日*/@Service("sessionTokenProcesser")public class SessionTokenProcesser extendsTokenProcesser {

@Autowired

HttpServletResponse response;

@Overridepublic booleanvalidToken(HttpServletRequest request) {

String clientToken=getClientToken(request);if(StringUtils.isEmpty(clientToken)) {return false;

}

HttpSession session= request.getSession(false);if (session == null) {return false;

}

String tokenKey=getTokenKey(request);

Object tokenObj=session.getAttribute(tokenKey);if(tokenObj==null)

{

rethrow("服务端不存在当前token,请重新请求表单");

}

String serverToken=tokenObj.toString();

session.removeAttribute(tokenKey);

System.out.println("remove server token:" +serverToken);returnclientToken.equals(serverToken);

}

@OverridepublicString getTokenKey(HttpServletRequest request) {returngetTokenField();

}

@Overridepublic voidsaveToken(HttpServletRequest request) {

HttpSession session=request.getSession();

String tokenKey=getTokenKey(request);

Object tokenObj=session.getAttribute(tokenKey);

String token;if (tokenObj == null) {

token=MakeToken.getInstance().getToken();//服务端保存token

session.setAttribute(tokenKey, token);

}else{

token=tokenObj.toString();

}

System.out.println("current token:" +token);//写入cookie

Cookie cookie = newCookie(getTokenField(), token);

response.addCookie(cookie);

}private voidrethrow(String message) {

RuntimeException e= newRuntimeException(message);throwe;

}

@OverridepublicString getClientToken(HttpServletRequest request) {

Cookie[] cookies=request.getCookies();if (cookies == null) {

rethrow("没有读取到客户端的cookie");return null;

}for(Cookie cookie : cookies) {if(cookie.getName().equals(getTokenKey(request))) {

String cookieValue=cookie.getValue();returncookieValue;

}

}

rethrow("客户端cookie中没有存储token");return null;

}

}

View Code

java防止表单二次提交_防止表单重复提交相关推荐

  1. mysql 防重复提交_怎样防止刷新重复提交、防后退

    怎样防止刷新重复提交.防后退 提交后禁用提交按钮 1.如果提交后,按F5刷新怎么办? 使用Session 在提交的页面也就是数据库处理之前: if session("ok")=tr ...

  2. java表单防重复提交_防止表单重复提交的解决方案整理

    用户在操作表单Post数据时往往会出现表单数据重复提交的问题,尤其在Web开发中此类问题比较常见.刷新页面,后退操作以前的页面,单机多次按钮都会导致数据重复提交.此类问题是因为浏览器重复提交HTTP请 ...

  3. 前后端分离重复提交_防止表单重复提交(二)

    实现原理: 1.页面和后台同步存入一个token,一旦刷新页面,此token都是会刷新的 2.提交表单时,会带上这个标识token 3.请求后台,将此token和后台存入的token比对 3.1 校验 ...

  4. python表单防重复提交_防止表单重复提交的几种策略

    表单重复提交是在多用户Web应用中最常见.带来很多麻烦的一个问题.有很多的应用场景都会遇到重复提交问题,比如: 点击提交按钮两次. 点击刷新按钮. 使用浏览器后退按钮重复之前的操作,导致重复提交表单. ...

  5. springboot 订单重复提交_防止表单重复提交(springboot,redis)

    我们在web项目中经常需要在后台对用户提交的表单进行校验防止重复提交.下面通过springboot的aop.redis来解决表单重复提交的问题. 通过在controller加上CheckSubmitF ...

  6. 分布式锁防止订单重复提交_防止表单重复提交看这里!!!

    要解决重复提交这事,先要知道什么是重复提交 假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许 ...

  7. 提交表单数据到数据库_普通表单不仅适用于数据库

    提交表单数据到数据库 您也可以将类似的规则应用于数据对象类型. (You can apply similar rules to data object types, too.) You probabl ...

  8. php ajax jquery 表单重复提交,jQuery如何防止Ajax重复提交

    首先说说防止重复点击提交是什么意思. 我们在访问有的网站,输入表单完成以后,单击提交按钮进行提交以后,提交按钮就会变为灰色,用户不能再单击第二次,直到重新加载页面或者跳转.这样,可以一定程度上防止用户 ...

  9. mysql动态表单设计与实现_动态表单的数据库结构设计

    利用在线编辑器设计的表单,包含输入框,明细表(动态添加行)等需要存储到数据库的信息,现在有三种思路: 1.一个表单对应数据库的一张或多张物理表(主从表),这种设计在很多业务的情况下,其数据库的物理表会 ...

最新文章

  1. 趣AI | 谁说失去手臂就不能弹琴了,有AI啊
  2. 一个想法照进现实-《IT连》创业项目:直觉型面试招聘的Bug
  3. @Slf4j注解介绍
  4. java中创建两种线程的方式_java中创建线程的两种方式有什么区别?
  5. kubernetes-Pod定义
  6. SpringCloud Greenwich(七)集成dubbo先启动消费者(check=false),然后启动提供者无法自动发现注册
  7. 第一太阳能公司(First Solar)在罗斯资本公司的评级上调
  8. Android 意图和意图过滤器(二)
  9. nginx日志统计分析的相关常用命
  10. 使用 APPLY 来为每行调用表值函数
  11. TypeError: can only concatenate list (not “int“) to list
  12. 一个一本正经的科普--5G是什么?
  13. 【Windows系统】产品ID、设备ID等系统参数
  14. Android 样式系统 | 常见的主题背景属性
  15. 【原创纯手打】如何使用Vue写微信朋友圈中的留言回复功能(附源码)
  16. C#笔试题面试题锦集
  17. 嵌入式linux内核启动过程,嵌入式Linux:ARM Linux启动流程
  18. linux下贪吃蛇代码,贪吃蛇 linux 程序
  19. 短信防火墙使用教程(短信防轰炸、防盗刷)
  20. Google Earth Engine(GEE)——User memory limit exceeded(2)

热门文章

  1. 415. Add Strings
  2. 【量子位节选摘抄】张亚勤:未来10年AI+生物制药大有可为,我们正开展破壁计划
  3. spark从入门到精通spark内存管理详解- 堆内堆外内存管理
  4. 深度学习核心技术精讲100篇(六十)-深度学习分类算法之神经网络
  5. 产品经理必备知识之网页设计系列(一)-创建出色用户体验
  6. 拉格朗日插值法的MATLAB源程序
  7. ZooKeeper编程
  8. Python零碎知识(11):assert用法
  9. python 标准差Std() 参数决定有偏或无偏
  10. 【机器学习PAI实践六】金融贷款发放预测