关于防止表单重复提交
Struts的Token(令牌)机制能够很好的解决表单重复提交的问题,基本原理是:服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。
这时其实也就是两点,第一:你需要在请求中有这个令牌值,请求中的令牌值如何保存,其实就和我们平时在页面中保存一些信息是一样的,通过隐藏字段来保存,保存的形式如: 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,这个value是TokenProcessor类中的generateToken()获得的,是根据当前用户的session id和当前时间的long值来计算的。第二:在客户端提交后,我们要根据判断在请求中包含的值是否和服务器的令牌一致,因为服务器每次提交都会生成新的Token,所以,如果是重复提交,客户端的Token值和服务器端的Token值就会不一致。下面就以在数据库中插入一条数据来说明如何防止重复提交。
在Action中的add方法中,我们需要将Token值明确的要求保存在页面中,只需增加一条语句:saveToken(request);,如下所示:
public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) //前面的处理省略 saveToken(request); return mapping.findForward("add"); }在Action的insert方法中,我们根据表单中的Token值与服务器端的Token值比较,如下所示: public ActionForward insert(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) if (isTokenValid(request, true)) { // 表单不是重复提交 //这里是保存数据的代码 } else { //表单重复提交 saveToken(request); //其它的处理代码 } }
其实使用起来很简单,举个最简单、最需要使用这个的例子:
一般控制重复提交主要是用在对数据库操作的控制上,比如插入、更新、删除等,由于更新、删除一般都是通过id来操作(例如:updateXXXById, removeXXXById),所以这类操作控制的意义不是很大(不排除个别现象),重复提交的控制也就主要是在插入时的控制了。
先说一下,我们目前所做项目的情况:
目前的项目是用Struts+Spring+Ibatis,页面用jstl,Struts复杂View层,Spring在Service层提供事务控制,Ibatis是用来代替JDBC,所有页面的访问都不是直接访问jsp,而是访问Structs的Action,再由Action来Forward到一个Jsp,所有针对数据库的操作,比如取数据或修改数据,都是在Action里面完成,所有的Action一般都继承BaseDispatchAction,这个是自己建立的类,目的是为所有的Action做一些统一的控制,在Struts层,对于一个功能,我们一般分为两个Action,一个Action里的功能是不需要调用Struts的验证功能的(常见的方法名称有add,edit,remove,view,list),另一个是需要调用Struts的验证功能的(常见的方法名称有insert,update)。
就拿论坛发贴来说吧,论坛发贴首先需要跳转到一个页面,你可以填写帖子的主题和内容,填写完后,单击“提交”,贴子就发表了,所以这里经过两个步骤:
1、转到一个新增的页面,在Action里我们一般称为add,例如:
public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { //这一句是输出调试信息,表示代码执行到这一段了 log.debug(":: action - subject add"); //your code here //这里保存Token值 saveToken(request); //跳转到add页面,在Structs-config.xml里面定义,例如,跳转到subjectAdd.jsp return mapping.findForward("add"); } 2、在填写标题和内容后,选择 提交 ,会提交到insert方法,在insert方法里判断,是否重复提交了。 public ActionForward insert(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response){ if (isTokenValid(request, true)) { // 表单不是重复提交 //这里是保存数据的代码 } else { //表单重复提交 saveToken(request); //其它的处理代码 } }
下面更详细一点(注意,下面所有的代码使用全角括号):
1、你想发贴时,点击“我要发贴”链接的代码可以里这样的:
〈html:link action="subject.do?method=add"〉我要发贴〈/html:link〉
subject.do 和 method 这些在struct-config.xml如何定义我就不说了,点击链接后,会执行subject.do的add方法,代码如上面说的,跳转到subjectAdd.jsp页面。页面的代码大概如下:
〈html:form action="subjectForm.do?method=insert"〉
〈html:text property="title" /〉
〈html:textarea property="content" /〉
〈html:submit property="发表" /〉
〈html:reset property="重填" /〉
〈html:form〉
如果你在add方法里加了“saveToken(request);”这一句,那在subjectAdd.jsp生成的页面上,会多一个隐藏字段,类似于这样〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,
2、点击发表后,表单提交到subjectForm.do里的insert方法后,你在insert方法里要将表单的数据插入到数据库中,如果没有进行重复提交的控制,那么每点击一次浏览器的刷新按钮,都会在数据库中插入一条相同的记录,增加下面的代码,你就可以控制用户的重复提交了。
if (isTokenValid(request, true)) { // 表单不是重复提交 //这里是保存数据的代码 } else { //表单重复提交 saveToken(request); //其它的处理代码 }
注意,你必须在add方法里使用了saveToken(request),你才能在insert里判断,否则,你每次保存操作都是重复提交。
记住一点,Struts在你每次访问Action的时候,都会产生一个令牌,保存在你的Session里面,如果你在Action里的函数里面,使用了saveToken(request);,那么这个令牌也会保存在这个Action所Forward到的jsp所生成的静态页面里。
如果你在你Action的方法里使用了isTokenValid,那么Struts会将你从你的request里面去获取这个令牌值,然后和Session里的令牌值做比较,如果两者相等,就不是重复提交,如果不相等,就是重复提交了。
由于我们项目的所有Action都是继承自BaseDispatchAction这个类,所以我们基本上都是在这个类里面做了表单重复提交的控制,默认是控制add方法和insert方法,如果需要控制其它的方法,就自己手动写上面这些代码,否则是不需要手写的,控制的代码如下:
public abstract class BaseDispatchAction extends BaseAction { protected ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { String parameter = mapping.getParameter(); String name = request.getParameter(parameter); if (null == name) { //如果没有指定 method ,则默认为 list name = "list"; } if ("add".equals(name)) { if ("add".equals(name)) { saveToken(request); } } else if ("insert".equals(name)) { if (!isTokenValid(request, true)) { resetToken(request); saveError(request, new ActionMessage("error.repeatSubmit")); log.error("重复提交!"); return mapping.findForward("error"); } } return dispatchMethod2(mapping, form, request, response, name); } }
关于防止表单重复提交相关推荐
- springboot 订单重复提交_Spring Boot (一) 校验表单重复提交
一.前言 在某些情况下,由于网速慢,用户操作有误(连续点击两下提交按钮),页面卡顿等原因,可能会出现表单数据重复提交造成数据库保存多条重复数据. 存在如上问题可以交给前端解决,判断多长时间内不能再次点 ...
- SpringMVC中实现的token,防表单重复提交
一:首先创建一个token处理类 ,这里的类名叫 TokenHandler private static Logger logger = Logger.getLogger(TokenHandler. ...
- 简单介绍redis分布式锁解决表单重复提交的问题
在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理.本文就详细的介绍一下redis分布式锁解决表单重复提交,感兴趣的可以了解一下 假 ...
- python表单防重复提交_防止表单重复提交的几种策略
表单重复提交是在多用户Web应用中最常见.带来很多麻烦的一个问题.有很多的应用场景都会遇到重复提交问题,比如: 点击提交按钮两次. 点击刷新按钮. 使用浏览器后退按钮重复之前的操作,导致重复提交表单. ...
- 表单重复提交的解决方法
表单重复提交的解决方法 参考文章: (1)表单重复提交的解决方法 (2)https://www.cnblogs.com/lwj-0923/p/7367517.html 备忘一下.
- 如何避免表单重复提交
客户端方案 禁掉提交按钮. 表单提交后使用Javascript使提交按钮disable.这种方法防止心急的用户多次点击按钮.但有个问题,如果客户端把Javascript给禁止掉,这种方法就无效了. 使 ...
- ASP.NET防止按F5键造成表单重复提交
F5键会引起表单重复提交,做过asp.net相信都会遇到过这个问题. 最有效的是一篇发表在MSDN的方法 原理如下: 在asp.net页面中有一个名为_VIEWSTATE的隐藏域,这个隐藏域保存着当前 ...
- 使用Struts2防止表单重复提交
用户重复提交表单在某些场合将会造成非常严重的后果.例如,在使用信用卡进行在线支付的时候,如果服务器的响应速度太慢,用户有可能会多次点击提交按钮,而这可能导致那张信用卡上的金额被消费了多次.因此,重复提 ...
- Struts2防止表单重复提交
最近开发中涉及到了表单重复提次的问题,通过研究做个总结. 防止表单重复提交主要用的到标签是<s: token />,拦截器 <interceptor-ref name="t ...
- 开发期间模板引擎页面修改以后,要实时生效 || 登陆成功,防止表单重复提交,可以重定向||只有登录之后才能访问相关的页面
去除模板引擎的缓存 th:if 优先级高于 th:text 登陆成功,防止表单重复提交,可以重定向到主页 只有登录之后才能访问相关的页面 login.html <!DOCTYPE html& ...
最新文章
- mysql答题表设计_PHP+MYSQL问答系统中的提问和回答的表怎么设计
- tensorflow中的lstm的state
- 使用mii-tool设置网卡速率
- 测试字符串读取类: TStringReader
- clion 格式化代码 设置空行 最多保留一行
- 代码逻辑分析_入行数据分析师不得不看的10本书
- 在生产环境中调试 Angular 应用程序而不显示源映射
- 在 VS 类库项目中 Add Service References 和 Add Web References 的区别
- 女程序员 一直从事着软件研发的工作
- 容器编排技术 -- Kubernetes 使用Service暴露应用
- 图像处理---《Canny 边缘检测》
- 看看20万码农怎么评论:女生做程序员是一种怎样的体验?
- java集合框架图(详细版)
- python期权定价公式_美式期权BAW定价的Python3实现
- TFT-LCD 液晶显示
- Pycharm一直显示connecting to console
- 深度测评小程序跨端开发框架
- 2020SCTF——PWN snake
- 中兴路由器查看端口详情命令
- 一个有点像舔狗的插件
热门文章
- XP系统硬盘安装Ubuntu14.04-超详细
- Python简单处理excel数据(拆分合并单元格、根据表头合并sheet、添加列数、添加内容操作)
- 分布式事务2PC论文翻译(Concurrency Control and Recovery in Database Systems )
- matlab赌徒破产模型转移矩阵,[转载]【转】生成土地利用变化转移矩阵的方法
- sketch 如何实现图片image圆角 头像圆角
- 华为鲲鹏ARM云服务器搭建网站服务器教程
- Java语言简介和基础
- spring data jpa 与 jpa的区别
- SQLMAP进阶:参数讲解
- 如何学习编程(编程如何学习呢)