防重复提交利器--struts2令牌

Posted 黑马程序员上海中心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了防重复提交利器--struts2令牌相关的知识,希望对你有一定的参考价值。

 通常在普通的操作当中,我们不需要处理重复提交的,而且有很多方法来防止重复提交。比如在登陆过程中,通过使用redirect,可以让用户登陆之上重定向到后台首页界面,当用户刷新界面时就不会触发重复提交了。或者使用token,隐藏在表单中,当提交时进行token验证,验证失败也不让提交。这都是一般的做法。

    我们这次碰到的问题是重复提交本身就是一个错误,重复提交会导致一些相关数据的逻辑不再正确。而这些重复提交并不是通过普通的刷新界面,或者两次点击按钮来进行的。在普通的操作当中,我们可以通过一系列的手段,使得相应参数被清零,从而防止数据上的不正确。但是,在一种情况下,这些手段都不再有效,那就是并发的重复提交。
    并发重复提交,那就是在同一时间内(时间间隔可以缩短到0.X秒之内),在这种情况下,所有的常规逻辑都不再有效,因为多个请求,同时进入系统,系统已不能判断出这些请求是否是无效的,它们同时通过常规的重复逻辑判断,并最终在同一时间内将数据写入到数据库中,引起数据错误。

    举一个简单的例子,在系统中销售一个商品,首先通过该商品id进入到系统逻辑判断,判断此商品是否已售出,如果未售出,就进行数据存取操作。商品是否售出,是一个逻辑判断,是验证数据存储到数据库的一道门。在常规的判断当中,前一请求通过这道门之后,后一请求就不能通过了,因为验证为false。但在并发请求中,两个或多个请求同时通过了这道门,因为都是同时进入到判断,在判断之前都验证商品没有被售出,所以就同时进入到数据的存储当中。

    在常规的java开发中,对于这种情况,临界资源,通常是使用加锁来保证这种情况的先后顺序。但是加锁有一个问题即是,它是对于全局信息的加锁,即对整个将要销售的商品进行加锁了。对于BS应用来说,我们必须保证另一个操作人员的同一种商品的销售请求通过,即只限制同一个操作人员销售的并发请求,不限制多个操作人员不同请求的处理。
    在这种情况下,我们的加锁就不能简单的锁定在商品上,而是要锁定在与操作人员有关的信息上,这就是session。

    session是一个在单个操作人员整个操作过程中,与服务器端保持通信的惟一识别信息。在同一操作人员的多次请求当中,session始终保证是同一个对象,而不是多个对象,因为可以对其加锁。当同一操作人员多个请求进入时,可以通过session限制只能单向通行。
    本文正是通过使用session以及在session中加入token,来验证同一个操作人员是否进行了并发重复的请求,在后一个请求到来时,使用session中的token验证请求中的token是否一致,当不一致时,被认为是重复提交,将不准许通过。

原理: 服务器端在处理客户端的请求之前,会将请求中包含的令牌值与保存在当前会话中的令牌值进行比较,看是否匹配。在处理完该请求后,并且在信息达到客户端之前,将产生一个新的令牌。该令牌值将会替换当前会话中的令牌值,并且传到客户端。这样如果用户回退到刚才的提交页面并再一次提交的话,客户端传过来的令牌与服务其中的令牌值不一致,从而有效的防止了提交。 实现:token拦截器
    整个流程可以由如下流程来表述:

  • 客户端申请token

  • 服务器端生成token,并存放在session中,同时将token发送到客户端

  • 客户端存储token,在请求提交时,同时发送token信息

  • 服务器端统一拦截同一个用户的所有请求,验证当前请求是否需要被验证(不是所有请求都验证重复提交)

  • 验证sessiontoken是否和用户请求中的token一致,如果一致则放行

  • session清除会话中的token,为下一次的token生成作准备

  • 并发重复请求到来,验证token和请求token不一致,请求被拒绝

    由以上的流程,我们整个实现需要以下几个东西

  • <s:token>,负责生成token

  • 客户token请求处理action,负责处理客户请求,并返回token信息

  • token拦截器,用于拦截指定的请求是否需要验证token

  • token请求拦截标识,用于标识哪些请求是需要被拦截的

  • 客户端token请求处理方法,用于请求token,并存放于特定操作中,并在提交时发送到请求中

jsp页面配置<s:token>标签:

<form action="user_login.action" method="post" >

   <s:token />

   <input name="name" type="text"> <br>

   <input name="password" type="password"><br>

   <input type="submit" value="登录"></input>

   &emsp;&emsp;<input type="reset" value="重置"></input>

</form>


struts.xml配置token拦截器:

<interceptor-ref name="token">

               <param name="excludeMethods">login</param>

</interceptor-ref>



  JSP使用<s:token/>标签的时候,Struts2会建立一个GUID(全局唯一的字符串)放在session中,并且会成为一个hidden放在form中。  

token拦截器会判断客户端form提交的token和session中保存的session是否equals。如果equals则执行Action。否则拦截器直接返回invaid.token结果,Action对应的方法也不会执行

一旦重复提交,页面提示错误信息:

HTTP Status 404 - No result defined for actionaa.UserAction and result invalid.token

配置invaild.token结果集解决问题.

    


以上是关于防重复提交利器--struts2令牌的主要内容,如果未能解决你的问题,请参考以下文章

关于struts2防止表单重复提交

struts2学习(15)struts2防重复提交

使用Redis实现接口防重复提交

使用Redis实现接口防重复提交

Struts2防止表单重复提交

12-struts2防止表单重复提交