zoukankan      html  css  js  c++  java
  • Struts2使用Token避免表单重复提交(十三)

    勿以恶小而为之,勿以善小而不为--------------------------刘备

    劝诸君,多行善事积福报,莫作恶

    上一章简单介绍了Struts2编写自定义验证拦截敏感词汇(十二),如果没有看过,请观看上一章

    一.为什么要进行表单验证

    在实际开发和生活中,当我们在一个表单填写好数据,进行提交时,如果这个时候网卡,我们一般会再次,甚至是多次点击提交按钮(以为这样会快),也有的会先将浏览的页面返回到上一步,再进行退回,或者重复点击回车, 这样会导致数据重复提交。

    为了防止表单重复提交,可以在客户端进行简单的处理,可以在服务器端进行处理。

    这里只说明在服务器端进行的处理。

    如果想了解客户端处理的,可以观看孤傲苍狼前辈的博客:https://www.cnblogs.com/xdp-gacl/p/3859416.html

    二 搭建Struts2的基本运行环境,演示重复提交

    二.一 创建UserAction

    里面有常用的五个方法。

    package com.yjl.web.action;
    import org.apache.log4j.Logger;
    import com.opensymphony.xwork2.ActionSupport;
    /**
    * @author 两个蝴蝶飞
    * @version 创建时间:2018年9月14日 下午6:35:10
    * 演示重复提交的Action.在添加用户是进行演示
    */
    public class UserAction extends ActionSupport {
    	private static Logger logger=Logger.getLogger(UserAction.class);
        private static final long serialVersionUID = -4164832837385401186L;
    	public String toAddUI(){
    		logger.info("跳转到添加学生的页面");
    		return "addUI";
    	}
    	public String add(){
    		logger.info("执行添加学生的操作");
    		return "toList";
    	}
    	public String toEditUI(){
    		logger.info("跳转到添加学生的页面");
    		return "editUI";
    	}
    	public String edit(){
    		logger.info("执行修改学生的操作");
    		return "toList";
    	}
    	public String delete(){
    		logger.info("执行删除学生的操作");
    		return "toList";
    	}
    	public String list(){
    		logger.info("执行查询学生的操作");
    		return "list";
    	}
    }
    

    二.二 配置struts.xml文件

    <package name="user" namespace="/" extends="struts-default">
    		<action name="User_*" class="com.yjl.web.action.UserAction" method="{1}">
    			<result name="addUI">/WEB-INF/content/add.jsp</result>
    			<result name="editUI">/WEB-INF/content/edit.jsp</result>
    			<result name="list">/WEB-INF/content/list.jsp</result>
    			<!-- 应该当添加成功之后,就跳转到List界面 -->
    			<result name="toList" type="chain">User_list</result>
    		</action>
    </package>
    

    二.三 编写前端页面

    二.三.一 编写 /content/add.jsp 页面

    <body>
    	<h3>这是一个添加学生的页面</h3>
    	<s:form action="User_add" method="post" namespace="/">
    		<s:textfield label="姓名" name="name"></s:textfield>
    		<s:submit value="添加学生"/>
    	</s:form>
    </body>
    
    

    二.三.二 编写 /content/edit.jsp 页面

    <body>
    	<h3>这是一个修改学生的页面</h3>
    	<s:form action="User_edit" method="post" namespace="/">
    		<s:textfield label="姓名" name="name"></s:textfield>
    		<s:submit value="添加学生"/>
    	</s:form>
    </body>
    

    二.三.三 编写 /content/list.jsp 页面

    <body>
    	<h3>这是一个显示学生的页面</h3>
    </body>
    

    二.四 重启服务器,运行程序

    输入网址: http://localhost:8080/Struts_Token/User_toAddUI

    有图片

    当输入名字之后 ,

    点击添加学生之后:

    日志栏:


    有图片


    页面上会显示:

    有图片


    下面会显示学生的信息,

    注意此时上面的地址栏是:User_add.action。

    二.五 出现的问题

    1. 此时如果用户将光标定位到地址栏, 继续点击回车的话,还会继续执行一遍add()的方法:

    在这里插入图片描述

    2.如果用户刷新浏览器的话,也会继续执行一遍add()的方法:

    在这里插入图片描述

    会发现根本原因主要是地址栏的原因。地址栏的地址仍然是 User_add.action, 而不是 User_list.action

    需要在 struts.xml中配置时type类型的值。

    二.六 将类型type改成redirectAction

    将toList 的返回类型,由默认的 chain改成redirectAction。

    <!--错误的用法:<result name="toList" type="chain">User_list</result>-->
    <result name="toList" type="redirectAction">User_list</result>
    

    二.七 修改后重新验证

    点击添加学生按钮:

    在这里插入图片描述

    再次点击回车按钮时:

    在这里插入图片描述

    刷新浏览器时:

    在这里插入图片描述

    都只是执行list()的方法,并不会再次执行add()的方法。

    三. 方法运行时间过长导致的问题

    你以为只是改成一个type值就万事大吉了吗?你太天真了。

    如果添加add()这个方法运行的时候够长的话,仍然会有一些错误的。

    实际情况中,add()这个方法会执行很多的逻辑验证,并不是只改变一个表,所以运行时间可能会长。

    用Thread线程的睡眠来模拟这种情况。

    三.一 在 add() 方法中,添加延迟处理

    在add()方法中添加一个休眠的处理:

    public String add(){
    	try {
    		Thread.sleep(5000);
    	} catch (InterruptedException e) {
    		e.printStackTrace();
    	}
    	logger.info("执行添加学生的操作");
    	return "toList";
    }
    

    三.二 重启服务器,再次验证

    重启服务器,进行相应的验证:

    点击一次添加后,浏览器在转圈,用户非常有可能再点一次,那么就会产生这种情况:

    在这里插入图片描述

    很明显,执行了两次添加学生的操作。 这样,在展示数据的时候,就会展示两条, 而用户明明只添加了一条。

    这是非常不可行的。

    以前由于添加时时间短,没有发现这个问题。现在发现了,必须要去除。

    Struts2框架提供了拦截器,来避免这一点

    四. 利用 tokenSession 拦截器 防止表单提交

    这种方法是在用户要提交的表单中,加入一个<s:token>标签,这样,当浏览器第一次访问这个带有<s:token>标签的页面时,

    在服务器中,解析<s:token>标签的类(TokenTag.class),会生成一个随机的字符串(这个字符串,查看网页的源代码可以看到),

    并且发送给客户端的浏览器,同时,在服务器中,会把这个随机字符串保存到用户的session对象中。

    当第一次提交表单时,在服务器中,会比较客户端和服务器中分别保存的这个随机字符串,因为是第一次提交,所以这两个字符串相等,

    然后进行正常的业务处理。第一次提交后,在服务器中的session中保存的这个随机字符串,会改变为其他的随机值,注意,这是很重要的一步!

    此时,地址栏停留在处理用户提交数据的Action中,客户端中保存的随机字符串没有改变,若是刷新页面,

    即重复提交,服务器再进行两个字符串的比较,会不相等,就会跳转到name为invalid.token的结果页面中,这样就会防止表单重复提交了。

    (摘录于hackerain前辈的博客:https://blog.csdn.net/hackerain/article/details/6990121)

    四.一 在 add.jsp 表单中添加<s:token/>

    在add.jsp页面添加<s:token></s:token>

    <body>
    	<h3>这是一个添加学生的页面</h3>
    	<s:form action="User_add" method="post" namespace="/">
    		<!-- 添加一个token -->
    		<s:token></s:token>
    		<s:textfield label="姓名" name="name"></s:textfield>
    		<s:submit value="添加学生"/>
    	</s:form>
    </body>
    

    Struts2中已经实际上token的操作,将其转成了一个拦截器。在package包下引用这个拦截器即可。

    四.二 配置struts.xml文件

    <package name="user" namespace="/" extends="struts-default">
    		<action name="User_*" class="com.yjl.web.action.UserAction" method="{1}">
    			<interceptor-ref name="tokenSession">
    	 			<!-- token对哪些方法起作用 -->
    	 			<param name="includeMethods">add,edit,delete</param>
    	 		</interceptor-ref>
    			<interceptor-ref name="defaultStack"></interceptor-ref>
    		    
    			<result name="addUI">/WEB-INF/content/add.jsp</result>
    			<result name="editUI">/WEB-INF/content/edit.jsp</result>
    			<result name="list">/WEB-INF/content/list.jsp</result>
    			<!-- 应该当添加成功之后,就跳转到List界面 -->
    			<result name="toList" type="redirectAction">User_list</result>
    			
    		</action>
    </package>
    

    四.三 重启服务器,进行验证Token

    输入网址: http://localhost:8080/Struts_Token/User_list.action

    输入姓名后, 正常点击一次添加学生的按钮:

    在这里插入图片描述

    发现正常的跳转和使用。

    点击两次或者多次添加学生的按钮时:

    在这里插入图片描述

    添加学生的操作也只添加了一次。

    进行修改的话,多次点击,也是只修改一次。

    完成正常的逻辑和功能操作。

    本章节的代码链接为:

    
    链接:https://pan.baidu.com/s/1lo58hZkgq9K0uyyMZLnGmg 
    提取码:09hl
    

    谢谢您的观看!!!

  • 相关阅读:
    ASP.NET 表单验证 Part.1(理解表单验证)
    Silverlight 简介 Part.3(设计 Siverlight 页面)
    ASP.NET 成员资格 Part.3(LoginStatus、LoginView、PasswordRecovery)
    ASP.NET 网站部署 Part.1(安装IIS、复制文件部署网站)
    ASP.NET Dynamic Data Part.1(创建动态数据应用程序)
    ASP.NET 安全模型 Part.2(SSL)
    ASP.NET MVC Part.2(扩展基本的 MVC 应用程序)
    ASP.NET 网站部署 Part.2(使用 Web 部署)
    开发高级 Web 部件
    创建 Web 部件(WebPart 类、简单的 Web 部件)
  • 原文地址:https://www.cnblogs.com/yjltx/p/13071855.html
Copyright © 2011-2022 走看看