Struts2中防止表单重复提交的两种方式及token拦截器的原理及作用
当用户填写完表单后,在提交过一次后,若用户做如下操作比如再次点击提交、刷新页面、提交页面呈现后点击后退按钮,都会导致表单重复提交。如果信息需要存储到后台数据库中,重复提交就会再次向数据库中插入用户信息,显然这样是不对的。解决方式有两种
第一种方法,Action处理完用户提交的数据后,重定向到另一个Action或是一个页面,使用户提交后,所停留的位置,不是当前处理数据的Action,这样用户再刷新时,就不会再次执行这个Action了,就会避免表单重复提交的问题了。
(二)Session Token机制
当用户首次访问包含表单的页面时,此时服务器会做三件事
1)创建一个session对象,
2)通过解析<s:token>标签(编程时此标签在要加入表单页面中)会产生一个随机数(可以将其称为盾牌,注意这个不是session ID)保存在session中
3)服务器把产生的随机数发送给客户端
当用户向服务器提交表单的时候,此时服务器会做
1)判断从客户端发送过来的请求参数中的随机数和保存在session对象中随机数是否相等,如果相等,则认为是第一次提交。
2)若是第一次提交,服务器会把原来保存在session的随机数改变成其他的随机数。当时发送给客户端的随机数不变。
3)因为在第一次提交表单后,服务器端地随机数和客户端的不一样了,所以当重复提交的时候,服务器看到客户端的随机数和自己的不一样了就可以判断这是在重复提交了。
代码实现:
1、表单页面,注意:在使用session token时,必须用struts2表标签库
<%@taglib prefix="s" uri="/struts-tags" %>
register1.jsp页面
- <s:form action="token" theme="simple">
- username:<s:textfield name="username"></s:textfield><br/>
- password:<s:password name="password"></s:password><br/>
- <s:submit value="submit"></s:submit>
- <s:token></s:token> <!--服务器通过token标签 来产生盾牌随机数-->
- </s:form>
- <action name="token" class="com.struts2.TokenAction">
- <result name="success">/success.jsp</result>
- <result name="invalid.token">/invalid.jsp</result>
- <interceptor-ref name="token"></interceptor-ref>
- <interceptor-ref name="defaultStack"></interceptor-ref>
- </action>
TokenAction
- package com.struts2;
- import com.opensymphony.xwork2.ActionSupport;
- public class TokenAction extends ActionSupport
- {
- private String username;
- private String password;
- public String getUsername()
- {
- return username;
- }
- public void setUsername(String username)
- {
- this.username = username;
- }
- public String getPassword()
- {
- return password;
- }
- public void setPassword(String password)
- {
- this.password = password;
- }
- @Override
- public String execute() throws Exception
- {
- return SUCCESS;
- }
- }
- <body>
- 注册成功!
- </body>
- <body>
- 不能重复提交!
- </body>
总的来讲,代码部分xvyao注意的
1、在含有form表单的页面注意使用struts2表标签库
2、在含有form表单的页面加入<s:token></s:token> 标签
3、在struts.xml页面中配置:
<result name=”token.invalid”>/invalid.jsp</rssult>
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
众所周知struts2有着自己的token拦截器,主要是用于防止用户进行表单的重复提交的。但是一直没搞明白两个问题:token拦截器实现的原理是如何的?哪些action需要使用token拦截器去防止其重复提交?下面根据自己最近的学习给出一个自己的经验。
首先,实现token拦截器基本需要两步:
1、在jsp的form表单任何地方中加一句<s:token></s:token>,这个标签的作用是在表单中生成一个隐藏的token属性,其值为一串随机数,这个随机数在你每一次刷新该页面或者打开该页面都会重新随机生成,如下:(可在网页上查看源码或者审查元素发现)
- <input type="hidden" name="struts.token.name" value="token">
- <input type="hidden" name="token" value="7KCFGRK6J0X99Q8G97ICL2NX4HWVXE81">
2、在struts配置文件struts.xml中为token拦截器添加拦截参数,并添加拦截结果<result name="invalid.token"></result>,如下:
- <package name="student" namespace="/student" extends="struts-default">
- <interceptors> //配置拦截器
- <interceptor-stack name="tokenStack"> //命名拦截器栈,名字随便
- <interceptor-ref name="token"/> //此拦截器为token拦截器,struts已经实现
- <interceptor-ref name="defaultStack" /> //默认拦截器,注意顺序,默认拦截器放在最下面
- </interceptor-stack>
- </interceptors>
- <default-interceptor-ref name="tokenStack" /> //让该包中所有action都是用我们配置的拦截器栈,名字和上面的对应
- <action name="changeinfo" class="sChangeInfoAction">
- <result name="success">/WEB-INF/student/person_info.jsp</result>
- <result name="invalid.token">/WEB-INF/student/person_info.jsp</result> //这个结果是拦截后返回的页面,必须要配置
- </action>
- </package>
第一个问题:token拦截器实现的原理是如何的?
所谓的token,中文译名“令牌”。顾名思义,就是使用一种验证,验证对了,就让你继续访问action,验证错了,直接把你拦截住,交给指定的页面。验证的关键步骤就是token拦截器的原理:当浏览器访问一个带有<s:token></s:token>标签的页面时,服务器生成一个随机数,把这个随机数放入session中,也放入表单中隐藏的token属性中。显然,此时这两个随机数是相等的。而后,当你向服务器提交表单时,表单中隐藏的token属性也会传给服务器。此时如果访问的的action使用了token的拦截器(即上面配置的),那么服务器会从session中得到刚才的随机数,将其和表单的token属性比较,看两者是否相同。如果相等,表示此次访问不是重复提交,无需拦截,清空session中的随机数,接着正常访问action。如果不相等,则直接拦截,不会继续访问action,直接跳到<result name="invalid.token">指定的页面,并且session中的随机数不变。
至于为什么验证通过后要清空session中的随机数值呢?因为重复提交必然有一个特性:它的token属性是上一个,因此清空session中的随机数后就保证了不相等。想要通过验证则必须重新加载带有<token>标签的jsp。
下面盗两张图片增加理解:
token生成原理图
token拦截原理图
第二个问题:哪些action需要使用token拦截器去防止其重复提交?
我在项目中token一般用在向数据库写数据的action,而对于向数据库读取数据的action则不用,具体情况具体分析。另外,在url中直接访问action也会导致拦截,因为服务器无法接收到form中的token属性,因此只有提交form才有机会访问action。