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
    

    谢谢您的观看!!!

  • 相关阅读:
    算法时间测试
    HDU1164
    git 中 HEAD detached from 802e836
    mysql中的substring()截取字符函数
    git分支/标签操作
    git简介、基本命令和仓库操作
    markdown编辑器学习
    数据库的三范式
    switch语句能否作用在byte,long,string上
    Spring的优缺点
  • 原文地址:https://www.cnblogs.com/yjltx/p/13071855.html
Copyright © 2011-2022 走看看