利用Token防止重复提交(Struts框架)

来源:互联网 发布:linux重新启动命令 编辑:程序博客网 时间:2024/05/21 07:14
如果用户对一个html表单多次提交,web应用应该能够判断用户的重复提交行为,并作出相应的处理。 最常见的是新增一条数据,用户已经提交表单并且服务器端已经完成新增成功。此时用户可能有两个误操作:
1.用户通过浏览器的后退功能,返回到录入页面,重复提交(此时浏览器提供回退功能基本上是个邪恶行为)
2.刷新该页面(因为新增成功的提示页面通常是通过请求转发(forward)过来的,所以此操作实际效果通常等同于1)
 这样造成的可能结果有:
1.若程序级别和数据库级别限制了重复记录,会提示类似于“xxx字段已存在,请修改后重新保存”的信息
2.若没有此限制,服务器端会再插入一条数据,而这通常不是用户想要的
误操作2和可能结果2的结合就达成了与用户意图相背的结果:服务器端不停地在增加重复记录,用户认为自己只不过是刷新该提示信息页面。
通用的解决思路是:
用户请求录入页面,这个与服务器建立的一次连接过程中,
在服务器端①【生成一个session标识,同时返回到客户端一个与此匹配的hidden域】。
用户提交了此页面,服务器端首先②【判断此hidden域与session标识是否匹配】,
若不匹配,终止保存操作,提示同一表单不能提交两次,
同时①【新建一个session标识和hidden域】,返回录入页面;
若匹配,执行插入保存操作,同时③【清空(重置reset)session标识】。
Struts正在基于这样的思路在org.apache.struts.action.Action类中提供了内置支持方法:
protected void saveToken(HttpServletRequest request) 配合标签对应于①
protected boolean isTokenValid(HttpServletRequest request) 对应于②
protected void resetToken(HttpServletRequest request) 对应于③
 这样我们在写程序的时候,结合Struts的html标签,
只要 1.在forward到insert.jsp页面前加一个action执行saveToken(request)操作,或干脆在insert.jsp中写
2.保存前加个判断操作isTokenValid(request)
3.若isTokenValid(request)返回false,执行saveToken(request)操作,返回错误提示页面;true则执行resetToken(request)操作,然后进行实际的保存操作 。
1.问题的产生原因
      用Struts来实现MVC框架。这时页面提交给Action去进行业务处理,Action再跳转回前台页面,但这时URL依然是“页面提交给Action的链接”,这时前台刷新一下页面,就变成再次执行了一次提交操作;
2.解决思路
      2.1在Action页面中跳转的时候用重定向,可以在struts_config.xml中配置<forward ... redirect="true">            不过这种方法会使得Request中放置数据丢失;
      2.2用Token令牌环来实现(这个操作也相对简单,本文章就来实现这个功能);            提交到Action的时候,进行一系列操作,然后保存一个标志,这时再跳转到前台页面(必须用struts标签,程序会自动在该前台页面设置一个隐藏域)。如果前台页面刷新的话,Action通过查看是否有标志,就能判断用户是刷新还是提交。
3.做个Token例子(模拟一个插入数据的操作,插入数据后,返回到列表页面,这时刷新列表页,看插入操作会不会多次执行)
      3.1工程名为SubmitAgain,文件:AddUserAction.java,AddUserForm.java,index.jsp,add.jsp,list.jsp,struts_config.xml
      3.2AddUserAction.java的内容如下:            public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)
            {                  AddUserForm addUserForm = (AddUserForm) form;
                  switch (addUserForm.getType())                  {
                        case 1:// 跳转到输入插入信息页面                              this.saveToken(request);                              return mapping.findForward("goadd");
                        case 2:// 插入信息                              if (this.isTokenValid(request))                              {                                    System.out.println("插入");                                    this.resetToken(request);                              }
                              else                              {                                    System.out.println("你在刷新页面");                              }
                              return mapping.findForward("golist");                  }
                  return null;            }      3.3AddUserForm.java里面就一个参数type及其set和get方法;
      3.4Struts_config.xml的内容如下:
            <action attribute="addUserForm" name="addUserForm" path="/addUser" scope="request" type="com.wangwz.struts.action.AddUserAction">                  <forward name="goadd" path="/add.jsp"></forward>                  <forward name="golist" path="/list.jsp"></forward>            </action>
      3.5index.jsp的内容如下:
            <a href="./addUser.do?type=1">插入</a>
      3.6add.jsp的内容如下:
            <html:form action="/addUser.do">                  <html:hidden property="type" value="2" />                  <html:submit value="插入" />            </html:form>
      3.7list.jsp页面中随便输入几个字即可
      3.8代码写完后,访问http://localhost:8080/SubmitAgain即可