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

谢谢您的观看!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

两个蝴蝶飞

你的鼓励,是老蝴蝶更努力写作的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值