struts2的token实现
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Struts2中使用token避免重复提交的方法
作者: 佚名, 出处:IT专家网,责任编辑: 谢妍妍,
2009-09-22 13:00
本文Struts2中使用token避免重复提交的方法
1.在struts.xml中
<action name="register" class="org.sunxin.struts2.action.RegisterAction"> <!-- 配置异常映射,当RegisterAction抛出Exception异常时,向用户显示error.jsp页面-->
<exception-mapping result="error" exception="ng.Exception"/>
<result name="input">/pages/register.jsp</result>
<result name="success">/pages/success.jsp</result>
<result name="error">/pages/error.jsp</result>
<result name="invalid.token">/pages/register.jsp</result>
<result name="wait">wait.jsp</result>
<interceptor-ref name="defaultStack"></interceptor-ref>
<interceptor-ref name="token">
<param name="excludeMethods">input</param>
</interceptor-ref>
</action>
2.在页面中加
<s:actionerror/>
<s:form action="register" method="post">
<s:token></s:token>
</s:form>
3.
.<interceptor-ref name="token"/>
<interceptor-ref name="token-session"/>
<!--注意struts2.0 拦截器名字为token-session struts2.1.2 已经更改为tokenSession -->
token: 在活动中检查合法令牌(token), 防止表单的重复提交; 在会产生提示信息token-session: 同上, 但是在接到非法令牌时将提交的数据保存在session中; 不会在会产生提示信息
只会在后台发出警告并处理,如下:
警告: Form token KO80SIJW4F84034NG5HM1ZBUGOVNY64D does not match the session token null.
编辑推荐:
1. Struts2拦截器execAndWait简介
2. Struts2教程:第一个Struts2程序
3. Struts2对WebWork的改进
struts2使用拦截器来检查表单是否重复提交,它采用同步令牌的方式来实现对
表单重复提交的判断。
首先需要在表单中使用<s:token name="user.token"></s:token>
<s:token>标签创建一个新的令牌值,并用你所指定的令牌名把令牌保存到session中。
而这个令牌值是随即产生的经过加密的字符序列,不会重复。
其次需要为action配置TokenInterceptor或者TokenSessionStoreInterceptor拦截器。
这两个拦截器都已经在
struts-default.xml中定义,但没有包含在defaultStack拦截器栈中。
在action中配置拦截器和在重复提交时,将要请求导向的结果视图。
<action name="register" class="com.zhaosoft.action.RegisterAction"> <!-- 配置异常映射,当RegisterAction抛出Exception异常时,向用户显示error.jsp页面 -->
<exception-mapping result="error"
exception="ng.Exception"/>
<result name="invalid.token">/WEB-INF/pages/register.jsp</result>
<result name="input">/WEB-INF/pages/register.jsp</result>
<result name="success">/WEB-INF/pages/success.jsp</result>
<result name="error">/WEB-INF/pages/error.jsp</result>
<interceptor-ref name="defaultStack">
<param name="workflow.excludeMethods">default</param>
</interceptor-ref>
<interceptor-ref name="token">
<param name="excludeMethods">default</param>
</interceptor-ref>
</action>
注:excludeMethods指定要排除的方法。
在register.jsp页面中添加action级别的错误信息显示的标签:
<s:actionerror/>
在form中添加<s:token>标签:<s:token name="user.token"></s:token> 最好为在资源文件中设置键struts.messages.invalid.token的本地化消息。
struts.messages.invalid.token=您已经提交了表单,请不要重复提交。
TokenSessionStoreInterceptor:使用TokenSessionStoreInterceptor拦截器同样能避免重复提交,TokenSessionStoreInterceptor集成自TokenInterceptor。
使用TokenSessionStoreInterceptor将不会输出任何错误信息。
如果token无效,请求被导向到invalid.token结果码映射的视图。
配置如下:
<interceptor-ref name="tokenSession">
<param name="excludeMethods">default</param>
</interceptor-ref>
看了网上说的有关struts2不重复刷新有很多,基本上都是在同一个页面中可以,那么我说下在一个页面中有一个按钮当点击后,到另一个页面。
这个页面有许多列表记录,点击保存后再一次跳转到上一次页面中。
那么在当前页面点击右键刷新,问题就在这了。
以前的
html:
<s:form action="topic" method="post">
<s:token name=""/>
</s:form>
struts.xml:
<action name="topic_*" method="{1}" class="topicAction">
<result name="success">/topic/manage_topic.jsp</result>
<result name="invalid.token" type="dispatcher">/common/noBack.jsp</result>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token">
<param name="includeMethods">addTopicElement</param>
</interceptor-ref>
<interceptor-ref name="token">
<param name="excludeMethods">default</param>
</interceptor-ref>
</action>
注意:
【1】:
<s:token>标签创建一个新的令牌值,并用你所指定的令牌名把令牌保存到session中。
而这个令牌值是随即产生的经过加密的字符序列,不会重复。
其次需要为action配置TokenInterceptor或者TokenSessionStoreInterceptor拦截器。
这两个拦截器都已经在struts-default.xml中定义,但没有包含在defaultStack拦截器栈中。
【2】:
<interceptor-ref name="token"/>和<interceptor-ref name="defaultStack"/>是两个拦截器的配置,目的是为了接收页面传入
的token令牌及参数,<result name="invalid.token">/common/noBack.jsp</</result>指当发现重复提交时,需要流转到的页面.
【3】:
excludeMethods指定要排除的方法,includeMehtods是要拦截的方法。
如果是用javascript来提交的话,看下边----
function selectA() {
var url = "relation_addRelationshipTopic.do?relation.rnId=<%=rn != null ? rn.getId().longValue() : -1%>&relation.selectRn="+selectRn+"&fresh=" + Math.random()+"";
var token = "=relation.token";
var token2 = "relation.token=";
token2 += document.getElementsByName("relation.token")[0].value;
url += "&" + token + "&";
url += token2;
location = url;
}
注:
<s:token name="relation.token"/>
它回自动的生成两个hidden,一个是,一个是reletion.token.
struts2 <s:token/>标签
关键字: 防止表单重复提交
1、使用Struts2的表单标签,其中需要增加token标签。
如下:
……
Java代码
<%@ taglib uri="/struts-tags" prefix="s" %>
……
<s:form action="page1" theme="simple">
<s:datetimepicker name="order.date" label="购买日期" toggleType="explode" value="today"/><br/>
<s:token/>
<s:reset/><s:submit/>
</s:form>
<%@ taglib uri="/struts-tags" prefix="s" %>
……
<s:form action="page1" theme="simple">
<s:datetimepicker name="order.date" label="购买日期" toggleType="explode" value="today"/><br/>
<s:token/>
<s:reset/><s:submit/>
</s:form>
2、在struts配置文件中增加token拦截器。
如下:
Java代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"/dtds/struts-2.0.dtd">
<struts>
<package name="lee" extends="struts-default">
<action name="page1" class="org.bruce.Page1">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" />
<result>/page1.jsp</result>
<result name="invalid.token">/page1error.jsp</result>
</action>
</package>
</struts>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"/dtds/struts-2.0.dtd">
<struts>
<package name="lee" extends="struts-default">
<action name="page1" class="org.bruce.Page1">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" />
<result>/page1.jsp</result>
<result name="invalid.token">/page1error.jsp</result>
</action>
</package>
</struts>注意,需要name为invaid.token的result。
这是当拦截器判断是重复提交的时候,会转向的结果。
3、invaid.token页面打印错误信息,一样可以使用struts标签。
如下:
<s:actionerror/>
理解:
1、JSP使用<s:token/>标签的时候,Struts2会建立一个GUID(全局唯一的字符串)放在session 中,并且会成为一个hidden放在form中。
2、token拦截器会判断客户端form提交的token和session中保存的session是否equals。
如果equals则执行Action。
否则拦截器直接返回invaid.token结果,Action对应的方法也不会执行
---------------------------------------------------------------------------------
对于采用token防止表单重复提交的原理我就不用多说了,大家也应该都知道,在这我只介绍在struts2中如何利用标签实现防止表单的重复提交。
首先在表单中加入标签,会生成一个隐藏域用于存储系统自动随机生成的token值。
然后在action中启用TokenInterceptor,即在struts.xml中加入下面类似代码。
Java代码
1. <action name="register" class="UserAction" method="register">
2. <result>register_success.jsp</result>
3. <result name="input">register.jsp</result>
4. <result name="invalid.token">register.jsp</result>
5. <interceptor-ref name="token"></interceptor-ref>
6. <interceptor-ref name="defaultStack"></interceptor-ref>
7. </action>
<action name="register" class="UserAction" method="register">
<result>register_success.jsp</result>
<result name="input">register.jsp</result>
<result name="invalid.token">register.jsp</result>
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
</action>
其中<result name="invalid.token">register.jsp</result>”是在发生表单重复提交时,返回给用户提示信息的显示页面,同时还需在显示页面中加入;“<interceptor-ref name="token"></interceptor-ref>”是启用TokenInterceptor 如果表单重复提交,会提示The form has already been processed or no token was supplied, please try again。
修改国际化文件struts.messages.invalid.token 的键值,提供自己定制的错误信息。
比如:struts.messages.invalid.token= 您已经提交了表单,请不要重复提交。
在用struts2.0标签开发中,经常要使用form提交.
通常:
1)写一个action类
此action类完成提交后的保存动作.
2)写一个静态jsp页面
写form,以及输入input,和提交button.
3)设置struts*****.xml文件
配置action名称以及映射的类和返回结果名称等.
问题:
在点击"提交"后,我们通常会弹出一个提示信息的页面,用户此时有可能会按f5
刷新当前提交的action,从而将多个相同的数据保存到了后台数据库,并且造成
了潜在的安全危险!
如何避免重复提交呢?
1) 用<s:token>!
如在输入界面的jsp里,设置token标志:
<form name="myname" action="myaction.do" method="post">
<input type="text" name="stuName" value="" .../>
... ...
<s:token/>
<input type="button" onclick="mymethod(this.form);" .../>
</form>
2) 在struts****.xml文件里配置拦截器:
<!-- 保存 -->
<action name="Save"
class="com.yourcom.app.Action.Dataform.saveAction"
method="Save">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="token" />
<result
name="invalid.token">/Info/NoBack.jsp</result> </action>
3)写一个名称为NoBack.jsp的提示文件,当重复f5刷新时,将显示本页内容,提示"不可重复提交,或者页面已经过期"
<s:token/>在最终生成页面时,自动生成一个unique id,通过它拦截器才能知道页面是否是同一次提交.
注意自定义的表单域别重名了。
它的作用是防止表单重复提交,每次加载页面struts.token 的值都不一样,如果两次提交时该值一样,则认为是重复提交。
此时要启用 TokenInterceptor(token) 拦截器,最好是也启用TokenSessionStoreInterceptor(token-session) 拦截器,不然后台会出现错误提示:
2008-5-17 22:39:21
com.opensymphony.xwork2.interceptor.ParametersInterceptor setParameters
严重: ParametersInterceptor - [setParameters]: Unexpected Exception catched: Error setting expression 'struts.token' with value
'[ng.String;@1c2e163'
2008-5-17 22:39:21
com.opensymphony.xwork2.interceptor.ParametersInterceptor setParameters
严重: ParametersInterceptor - [setParameters]: Unexpected Exception catched: Error setting expression '' with value
'[ng.String;@abaf8c'
但不影响使用。
不过如果只有 token-session 拦截器却是不行的。
token 和 token-session 拦截器的启用,是在 struts.xml 配置文件中,既可以为包启用,也可以单独为某个 action 启用:
1) 为包启用 token 和 token-session
1.<package name="TestStruts" extends="struts-default">
2. <interceptors>
3. <interceptor-stack name="myStack">
4. <interceptor-ref name="token"/>
5. <interceptor-ref name="token-session"/>
6. <interceptor-ref name="defaultStack" />
7. </interceptor-stack>
8. </interceptors>
9. <default-interceptor-ref name="myStack" />
10. <action name="Login"
class="com.unmi.struts2.action.LoginAction">
11. <result name="input">/login.jsp</result>
12. <result name="invalid.token">/exception.jsp</result>
13. </action>
14................................................................
.............
<package name="TestStruts" extends="struts-default"> <interceptors> <interceptor-stack name="myStack"> <interceptor-ref name="token"/>
<interceptor-ref name="token-session"/> <interceptor-ref
name="defaultStack" /> </interceptor-stack> </interceptors>
<default-interceptor-ref name="myStack" /> <action name="Login"
class="com.unmi.struts2.action.LoginAction"> <result
name="input">/login.jsp</result> <result
name="invalid.token">/exception.jsp</result>
</action> ........................................................... .................
2) 为 Action 启用 token 和 token-session
1.<action name="Login"
class="com.unmi.struts2.action.LoginAction">
2. <interceptor-ref name="token" />
3. <interceptor-ref name="token-session" />
4. <interceptor-ref name="defaultStack" />
5. <result name="input">/login.jsp</result>
6. <result name="invalid.token">/exception.jsp</result>
7.</action>
<action name="Login" class="com.unmi.struts2.action.LoginAction">
<interceptor-ref name="token" /> <interceptor-ref name="token-session" /> <interceptor-ref name="defaultStack" /> <result
name="input">/login.jsp</result> <result
name="invalid.token">/exception.jsp</result> </action>
注意 token、token-session 和 defaultStack 的顺序要保证,还需要加上名为"invalid.token" 的 result,当发现重复提交时转向到这个逻辑页,如
/exception.jsp,在 /exception.jsp 加上 <s:actionerror /> 在出现重复提交时就会提示:The form has already been processed or no token was supplied, please try again.。