struts2令牌(token)内部原理

使用token标签的时候,Struts2会建立一个GUID(全局唯一的字符串)放在session中,并且会成为一个hidden放在form中。 
token拦截器会判断客户端form提交的token和session中保存的session是否equals。如果equals则执行Action。否则拦截器直接返回invaid.token结果,Action对应的方法也不会执行

struts.xml中配置  

<action name="userRegister" class="UserRegisterAction"> 
  <interceptor-ref name="defaultStack" />
  <interceptor-ref name="token" />
  <result name="invalid.token"> /jsp/service/power/login.jsp </result>
  <result name="success"> /jsp/service/power/login.jsp </result>
  <result name="fail"> /jsp/service/user/userRegister.jsp </result>
</action>

 使用<s:token>得到控制台提示信息:"Could not find token name in params“,后来发现原来是把<s:token>标签放在了<form>标签外面,导致出现以上问题。

<s:token>标签使用很简单,用以避免表单的重复提交,其原理是用户访问页面时,服务器产生一个令牌值一起发送给客户端。当这个页面被提交时,比较客户端和已经保存先前的令牌值,如果相等则清除令牌值,不相等表明表单已被提交,同时产生一个新令牌值,保存到session中

step 1:
  在<form>标签里面定义标签<s:token/> step 2:
  在struts.xml文件内配置token拦截器
<interceptor-ref name="token"> <param name="excludeMethods">....</param> //这里可以配置token拦截器的排队方法   </interceptor-ref>   <result name=“invaild.token">/***.jsp</result> step 3:
  可在页面中添加<s:actionerror
>显示重复提交信息 step 4:
  测试。提交页面后,单击刷新,就自动转到***.jsp

token 是在上一个action的执行方法后 会产生一个新的token放在session中 key 为“struts.token”  <s:token></s:token> 现实再页面的时候会生成俩个隐藏标签 一个叫struts.token.name  一个叫做是你struts.token.name  值作为name的隐藏标签,

提交到后台进行token 比对 不一样返回invaild.token

注:

1. 当使用token拦截器时,必须设置invalid.token的result,而使用tokenSession时,则不需要。

2. 使用token拦截器时,必须使用<s:token>标签,而且该标签必须包含在Form中。

//=============================================

struts2 token 原理
 
token 拦截器用来解决表单重复提交的问题, 什么情况下会造成表单重复提交
 
典型的场景:
 
配置
<action name="tokenPrepare" class="org.apache.struts2.showcase.token.TokenAction" method="add">
  <result name="input">add.jsp</result>
</action>
<action name="transfer" class="org.apache.struts2.showcase.token.TokenAction">
  <result name="invalid.token">doublePost.jsp</result>
  <result type="redirect" name="success">list.jsp</result>
</action>
一个TokenAction处理了表单提交逻辑后dispatcher到一个list.jsp页面。 用户如果刷新list页面时会出现重复提交表单问题
 
原因: struts2默认 dispatcher 为 内部重定向, 及 forward形式, 属于服务器端重定向

 

解决方法:
add.jsp表单内增加 <s:token/> 标记
transfer增加 token拦截器
<action name="transfer" class="org.apache.struts2.showcase.token.TokenAction">
  <interceptor-ref name="token"/>//没有添加默认拦截器会报错
  <result name="invalid.token">doublePost.jsp</result>
  <result name="success">list.jsp</result>
</action>
原理:?
<s:token/> 使得访问add.jsp时会在session中设置一个struts.token值
add.jsp表单中增加两个隐藏字段, 如:struts.token值随机生成
<input type="hidden" value="struts.token" name="struts.token.name">
<input type="hidden" value="FKZCDK5XC881W05MLVK0JQJP04M0KZC2" name="struts.token">
 
提交表单到 transfer Action时先会经过token拦截器, token拦截器会从form表单中获取struts.token值, 再从session中获取
struts.token值, 比较两个值是否相同, 不相同, 直接返回 invalid.token(invalid.token应配置成重复提交表单的提示信息页面)
相同,remove掉session里的 struts.token值(防止被重复使用), 继续正常流程
方法二:
对于表单操作, 重定向方式改成redirect就不会造成重复提交表单了。
<action name="transfer" class="org.apache.struts2.showcase.token.TokenAction">
<result name="invalid.token">doublePost.jsp</result>
<result type="redirect" name="success">list.jsp</result>
</action>
 
 
Token拦截器逻辑详见:
 
TokenInterceptor.java
String handleInvalidToken(ActionInvocation invocation)
令牌生成原理图:

    从图中可以看出,如果某个jsp页面中有token标签,那么无论是请求这个界面还是内部转发到这个界面,我们统一说成是“渲染界面”的时候,都会造成token id的产生或者更新。

   一定要搞清楚,这里是请求的jsp页面,此时可以产生令牌,但令牌不会起作用,因为它拦截的不是jsp,而是通过反射机制调用的方法。

 

令牌拦截原理图:

     从图中可以看出,令牌可以拦截的是Action中的方法。

     如果方法需要被拦截,会判断session中的token id和提交过来的token id是否相等。如果不相等,则直接跳转到预先配置好的界面,session中的token id不变;如果相等,则执行请求的方法,关键的一点是,此时session中的token id会被清空!这个步骤非常关键!

 

综上,关于令牌的使用,记住以下几点,基本可以应对各种复杂的令牌应用场景

 

    l  注意令牌的产生时机,它是在加载(或渲染)带有token标签的jsp页面时产生的,与请求或者转发无关,与方法是否被拦截无关。

    l  由于服务器端的token id是保存在session中的,因此不同的页面间可以共享,使用时注意,避免混乱。

    l  如果访问的方法属于被拦截的方法,验证通过之后,会清空session中的token id;如果验证不通过,session 中的token id不变,直到下一次加载(或渲染)带有token标签的jsp页面。

 

    再多啰嗦一些,为什么验证通过后要清空session中的token id呢?其实不难理解,从宏观上来思考,重复提交有一个必然的特征:它的token id是上一个。因此只要保证验证通过后session中保存的token id变化即可,清空是最直接的办法。要想通过验证,只有一个途径:重新加载(或渲染)带有token标签的jsp页面,使客户端的token id和服务器端的token id一致。

posted @ 2015-01-20 21:30  贝壳风铃  阅读(415)  评论(0)    收藏  举报