`

struts2防止表单重复提交

阅读更多
这是Struts2.1.8.1应用,关于表单重复提交的原理,参见代码注释

首先是web.xml文件

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="UTF-8"?> 
02.<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" 
03.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
04.    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee   
05.    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"> 
06.    <filter> 
07.        <filter-name>struts2</filter-name> 
08.        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 
09.    </filter> 
10.    <filter-mapping> 
11.        <filter-name>struts2</filter-name> 
12.        <url-pattern>/*</url-pattern> 
13.    </filter-mapping> 
14.    <welcome-file-list> 
15.        <welcome-file>testToken.jsp</welcome-file> 
16.    </welcome-file-list> 
17.</web-app> 
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>testToken.jsp</welcome-file>
</welcome-file-list>
</web-app>

然后是用于输入用户名和密码以测试重复提交表单的testToken.jsp页面

view plaincopy to clipboardprint?
01.<%@ page language="java" pageEncoding="UTF-8"%> 
02.<%@ taglib prefix="s" uri="/struts-tags"%> 
03. 
04.<h2>测试表单重复提交</h2> 
05. 
06.<s:actionerror/> 
07. 
08.<s:form action="testToken" theme="simple"> 
09.    <s:token name="hello"/> 
10.    姓名:<s:textfield name="username"/><br/> 
11.    密码:<s:password name="password"/><br/> 
12.    <s:submit value="提交"/> 
13.</s:form> 
14. 
15.<%-- 
16.==========================================================================================  
17.【表单重复提交】  
18.只需在<s:form/>中添加<s:token/>即可  
19.然后运行项目,打开该页面右键查看源文件发现<s:token/>转化成了如下代码  
20.<input type="hidden" name="struts.token.name" value="struts.token" /> 
21.<input type="hidden" name="struts.token" value="KF98D738ZRY8TNVQWJL3GYSB8LO5U748" /> 
22.其中struts.token.name是永远固定的,第一个hidden的value值与第二个hidden的name值是关联在一起的  
23.第二个hidden的value值是一个32位的随机的被加密过的字符串,由大写A到Z加上数字0到9组成  
24.另外,如果页面中写成<s:token name="hello"/>的话,那么再运行项目,查看该页面的源代码后发现  
25.此时的第一个hidden的value值与第二个hidden的name值都已经变成了hello了  
26.==========================================================================================  
27.【通过<s:token name="hello"/>举例说明】  
28.<input type="hidden" name="struts.token.name" value="hello"/> 
29.<input type="hidden" name="hello" value="HUZU1PLOZ2111AAN5XN48I5914IUM668"/> 
30.由于<s:token/>是个标签,所以必然存在相应的标签库的代码文件  
31.当服务器端解析到页面中的<s:token/>标签的位置时,该标签对应的类文件就会自动生成两个hidden  
32.第二个hidden的value属性对应的是Struts2产生的一个GUID值(Globally Unique Identifier全局唯一标识符)  
33.这个GUID是一个类似于HUZU1PLOZ2111AAN5XN48I5914IUM668的字符串  
34.然后Struts2会将HUZU1PLOZ2111AAN5XN48I5914IUM668放到HttpSession中,然后使用hello来标识它  
35.由于<s:token name="hello"/>是放在了表单里面,所以当我们提交表单的时候  
36.就会把两个hidden全部发送到服务器上,然后名为token的表单重复提交的拦截器就会截获struts.token.name  
37.如果截获到struts.token.name的话,就读取它的value值hello  
38.接着再以hello为name来读取HUZU1PLOZ2111AAN5XN48I5914IUM668  
39.然后就把HUZU1PLOZ2111AAN5XN48I5914IUM668值与已经放在HttpSession中的值进行比较  
40.如果两个值是一样的话,它就认为该表单是第一次提交,接着程序就正常执行  
41.如果第一次提交完毕之后,再去刷新页面或者进行第二次提交的话,仍然会发送一个被加密过的字符串  
42.当第一次提交成功之后,Struts2会将session里面的加密字符串删掉,也就是说session里面就不存在这个值了  
43.既然不存在这个值,那么客户端再发过来一个加密字符串的话。二者一比较,不一样,于是就认为这是第二次提交  
44.这样的话,就可以有效的对提交请求进行拦截。这个功能是通过拦截器来实现的  
45.这里用到的token拦截器,对应org.apache.struts2.interceptor.TokenInterceptor类  
46.它继承了MethodFilterInterceptor拦截器。另外token并没有配置在defaultStack拦截器里  
47.我们可以发现Struts2采用的防止表单重复提交的方式与Struts1是一模一样的,原理上没有任何改变  
48.但在使用方式上却简化又简化,不像Struts1那样还要写代码。显然比Struts1采用的token方式简洁得多得多  
49.==========================================================================================  
50.【重复提交表单时的提示信息的国际化】  
51.表单重复提交时的报错信息是存放在ActionError中的。在前台页面对错误信息进行输出结果如下所示  
52.The form has already been processed or no token was supplied, please try again.   
53.在org.apache.struts2.struts-message.properties中查找到了该信息对应的key  
54.然后我们就可以设置国际化信息,以输出中文提示  
55.==========================================================================================  
56.【tokenSession拦截器】  
57.使用token拦截器的做法在论坛中用得比较多,不过也有一些商务网站使用的是tokenSession拦截器  
58.商务网站通常会给用户一个提交成功的提示,而不管用户刷新多少次页面,它都会一直显示给用户提交成功的提示  
59.只不过这个成功提交可能会让用户产生误解罢了。事实上也仅仅提交成功了一次,后面的刷新操作都被忽略掉了  
60.这个功能就是由tokenSession拦截器实现的,使用这个拦截器不需要我们配置任何result,它返回的永远是成功页面  
61.也就是说,使用tokenSession拦截器时,前台不用做任何改变,还是在表单里面放置一个<s:token/>标签  
62.然后在struts.xml中写成<interceptor-ref name="tokenSession"/>即可  
63.如果发现用户重复提交表单数据的话,由于tokenSession拦截器的作用,它会返回result为success的页面  
64.我们也可以在Action的方法中打印输出一句话来测试是否仅仅第一次的提交被成功执行了  
65.经过测试得知,无论用户在客户端刷新几百遍几千遍,最后只有第一次的提交操作被执行了  
66.事实上后面的刷新的提交操作都被tokenSession拦截器给拦截了  
67.因为它发现客户端的session与服务器上的session不一样,于是就把后面的请求给拦截了  
68.==========================================================================================  
69.--%> 
<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>

<h2>测试表单重复提交</h2>

<s:actionerror/>

<s:form action="testToken" theme="simple">
<s:token name="hello"/>
姓名:<s:textfield name="username"/><br/>
密码:<s:password name="password"/><br/>
<s:submit value="提交"/>
</s:form>

<%--
==========================================================================================
【表单重复提交】
只需在<s:form/>中添加<s:token/>即可
然后运行项目,打开该页面右键查看源文件发现<s:token/>转化成了如下代码
<input type="hidden" name="struts.token.name" value="struts.token" />
<input type="hidden" name="struts.token" value="KF98D738ZRY8TNVQWJL3GYSB8LO5U748" />
其中struts.token.name是永远固定的,第一个hidden的value值与第二个hidden的name值是关联在一起的
第二个hidden的value值是一个32位的随机的被加密过的字符串,由大写A到Z加上数字0到9组成
另外,如果页面中写成<s:token name="hello"/>的话,那么再运行项目,查看该页面的源代码后发现
此时的第一个hidden的value值与第二个hidden的name值都已经变成了hello了
==========================================================================================
【通过<s:token name="hello"/>举例说明】
<input type="hidden" name="struts.token.name" value="hello"/>
<input type="hidden" name="hello" value="HUZU1PLOZ2111AAN5XN48I5914IUM668"/>
由于<s:token/>是个标签,所以必然存在相应的标签库的代码文件
当服务器端解析到页面中的<s:token/>标签的位置时,该标签对应的类文件就会自动生成两个hidden
第二个hidden的value属性对应的是Struts2产生的一个GUID值(Globally Unique Identifier全局唯一标识符)
这个GUID是一个类似于HUZU1PLOZ2111AAN5XN48I5914IUM668的字符串
然后Struts2会将HUZU1PLOZ2111AAN5XN48I5914IUM668放到HttpSession中,然后使用hello来标识它
由于<s:token name="hello"/>是放在了表单里面,所以当我们提交表单的时候
就会把两个hidden全部发送到服务器上,然后名为token的表单重复提交的拦截器就会截获struts.token.name
如果截获到struts.token.name的话,就读取它的value值hello
接着再以hello为name来读取HUZU1PLOZ2111AAN5XN48I5914IUM668
然后就把HUZU1PLOZ2111AAN5XN48I5914IUM668值与已经放在HttpSession中的值进行比较
如果两个值是一样的话,它就认为该表单是第一次提交,接着程序就正常执行
如果第一次提交完毕之后,再去刷新页面或者进行第二次提交的话,仍然会发送一个被加密过的字符串
当第一次提交成功之后,Struts2会将session里面的加密字符串删掉,也就是说session里面就不存在这个值了
既然不存在这个值,那么客户端再发过来一个加密字符串的话。二者一比较,不一样,于是就认为这是第二次提交
这样的话,就可以有效的对提交请求进行拦截。这个功能是通过拦截器来实现的
这里用到的token拦截器,对应org.apache.struts2.interceptor.TokenInterceptor类
它继承了MethodFilterInterceptor拦截器。另外token并没有配置在defaultStack拦截器里
我们可以发现Struts2采用的防止表单重复提交的方式与Struts1是一模一样的,原理上没有任何改变
但在使用方式上却简化又简化,不像Struts1那样还要写代码。显然比Struts1采用的token方式简洁得多得多
==========================================================================================
【重复提交表单时的提示信息的国际化】
表单重复提交时的报错信息是存放在ActionError中的。在前台页面对错误信息进行输出结果如下所示
The form has already been processed or no token was supplied, please try again.
在org.apache.struts2.struts-message.properties中查找到了该信息对应的key
然后我们就可以设置国际化信息,以输出中文提示
==========================================================================================
【tokenSession拦截器】
使用token拦截器的做法在论坛中用得比较多,不过也有一些商务网站使用的是tokenSession拦截器
商务网站通常会给用户一个提交成功的提示,而不管用户刷新多少次页面,它都会一直显示给用户提交成功的提示
只不过这个成功提交可能会让用户产生误解罢了。事实上也仅仅提交成功了一次,后面的刷新操作都被忽略掉了
这个功能就是由tokenSession拦截器实现的,使用这个拦截器不需要我们配置任何result,它返回的永远是成功页面
也就是说,使用tokenSession拦截器时,前台不用做任何改变,还是在表单里面放置一个<s:token/>标签
然后在struts.xml中写成<interceptor-ref name="tokenSession"/>即可
如果发现用户重复提交表单数据的话,由于tokenSession拦截器的作用,它会返回result为success的页面
我们也可以在Action的方法中打印输出一句话来测试是否仅仅第一次的提交被成功执行了
经过测试得知,无论用户在客户端刷新几百遍几千遍,最后只有第一次的提交操作被执行了
事实上后面的刷新的提交操作都被tokenSession拦截器给拦截了
因为它发现客户端的session与服务器上的session不一样,于是就把后面的请求给拦截了
==========================================================================================
--%>

然后是当用户名和密码正确时显示的result.jsp页面

view plaincopy to clipboardprint?
01.<%@ page pageEncoding="UTF-8"%> 
02.<h2>Login Success</h2> 
<%@ page pageEncoding="UTF-8"%>
<h2>Login Success</h2>

然后是Struts2的配置文件struts.xml

view plaincopy to clipboardprint?
01.<?xml version="1.0" encoding="UTF-8" ?> 
02.<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"  
03.                        "http://struts.apache.org/dtds/struts-2.1.dtd"> 
04. 
05.<struts> 
06.    <package name="struts2.1" extends="struts-default"> 
07.        <action name="testToken" class="com.jadyer.action.LoginAction" method="testToken"> 
08.            <result>/result.jsp</result> 
09.            <result name="invalid.token">/testToken.jsp</result> 
10.            <interceptor-ref name="token"/> 
11.            <interceptor-ref name="defaultStack"/> 
12.        </action> 
13.        <!-- 当Struts2发现用户进行表单重复提交时,它会寻找invalid.token --> 
14.        <!-- 并根据invalid.token找到与之对应的页面。所以记得要配置invalid.token结果 --> 
15.    </package> 
16.</struts> 
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">

<struts>
<package name="struts2.1" extends="struts-default">
<action name="testToken" class="com.jadyer.action.LoginAction" method="testToken">
<result>/result.jsp</result>
<result name="invalid.token">/testToken.jsp</result>
<interceptor-ref name="token"/>
<interceptor-ref name="defaultStack"/>
</action>
<!-- 当Struts2发现用户进行表单重复提交时,它会寻找invalid.token -->
<!-- 并根据invalid.token找到与之对应的页面。所以记得要配置invalid.token结果 -->
</package>
</struts>

最后是处理所输入的用户名和密码的LoginAction.java

view plaincopy to clipboardprint?
01.package com.jadyer.action;  
02. 
03.import com.opensymphony.xwork2.ActionSupport;  
04. 
05.@SuppressWarnings("serial")  
06.public class LoginAction extends ActionSupport {  
07.    public String testToken() throws Exception {  
08.        return SUCCESS;  
09.    }  
10.}


本文来自CSDN博客,出处:http://blog.csdn.net/jadyer/archive/2011/02/07/6174095.aspx
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics