【转】Struts中Token的使用方法

上一篇 / 下一篇  2009-07-15 11:04:33 / 个人分类:其他

Struts的Token(令牌)机制能够很好的解决表单重复提交的问题,基本原理是:服务器端在处理到达的请求之前,会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较,看是否匹配。在处理完该请求后,且在答复发送给客户端之前,将会产生一个新的令牌,该令牌除传给客户端以外,也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话,客户端传过来的令牌就和服务器端的令牌不一致,从而有效地防止了重复提交的发生。
c0uftyF%]-u]c?051Testing软件测试网a'Li'u s5u.Eu4}(P
这时其实也就是两点,第一:你需要在请求中有这个令牌值,请求中的令牌值如何保存,其实就和我们平时在页面中保存一些信息是一样的,通过隐藏字段来保存,保存的形式如: 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,这个value是TokenProcessor类中的generateToken()获得的,是根据当前用户的session id和当前时间的long值来计算的。第二:在客户端提交后,我们要根据判断在请求中包含的值是否和服务器的令牌一致,因为服务器每次提交都会生成新的Token,所以,如果是重复提交,客户端的Token值和服务器端的Token值就会不一致。下面就以在数据库中插入一条数据来说明如何防止重复提交。
1t We2F*~%N,gj051Testing软件测试网x+wg9h-}){p3L
在Action中的add方法中,我们需要将Token值明确的要求保存在页面中,只需增加一条语句:saveToken(request);,如下所示:51Testing软件测试网.~Ir;O)A}`'l0_
public ActionForward add(ActionMapping mapping, ActionForm. form,
G(|0Z!DR#_1@0HttpServletRequest request, HttpServletResponse response)
;nIt#y9`0//前面的处理省略
~t7j5[0x RtF0saveToken(request);51Testing软件测试网l6U9R3b{0i,O
return mapping.findForward("add");
2r{\-^HS0}在Action的insert方法中,我们根据表单中的Token值与服务器端的Token值比较,如下所示:
Vm iDV!v\*Hj0public ActionForward insert(ActionMapping mapping, ActionForm. form,51Testing软件测试网ycob1b1r:n8e
HttpServletRequest request, HttpServletResponse response)
} fj;X%Vip] Pgs0if (isTokenValid(request, true)) {51Testing软件测试网*}K-W:e?&{"O _
// 表单不是重复提交
r\ ~.x X+Lr6J k,nn2tI0//这里是保存数据的代码51Testing软件测试网Gz#w};jtH
} else {51Testing软件测试网6w6X/aN\WS
//表单重复提交
| C/} Z1h5~$i2~0saveToken(request);51Testing软件测试网+w/v7iTD;tX n
//其它的处理代码
5X|cEl#QG0}51Testing软件测试网p(o{D0p-X:G
}51Testing软件测试网Mm2q(m2o Vh
51Testing软件测试网IGSR0JF0nsY{U
其实使用起来很简单,举个最简单、最需要使用这个的例子:51Testing软件测试网i0j$C @%Yv.M'@
一般控制重复提交主要是用在对数据库操作的控制上,比如插入、更新、删除等,由于更新、删除一般都是通过id来操作(例如:updateXXXById, removeXXXById),所以这类操作控制的意义不是很大(不排除个别现象),重复提交的控制也就主要是在插入时的控制了。
V]8^'k P)Ex051Testing软件测试网r,m!s$E0d5Slk
先说一下,我们目前所做项目的情况:
p]"b;n!K]@0目前的项目是用Struts+Spring+Ibatis,页面用jstl,Struts复杂View层,Spring在Service层提供事务控制,Ibatis是用来代替JDBC,所有页面的访问都不是直接访问jsp,而是访问Structs的Action,再由Action来Forward到一个Jsp,所有针对数据库的操作,比如取数据或修改数据,都是在Action里面完成,所有的Action一般都继承BaseDispatchAction,这个是自己建立的类,目的是为所有的Action做一些统一的控制,在Struts层,对于一个功能,我们一般分为两个Action,一个Action里的功能是不需要调用Struts的验证功能的(常见的方法名称有add,edit,remove,view,list),另一个是需要调用Struts的验证功能的(常见的方法名称有insert,update)。
8C1Z"iR0TW(R]a051Testing软件测试网1uHz+d"rJ@bBe^
就拿论坛发贴来说吧,论坛发贴首先需要跳转到一个页面,你可以填写帖子的主题和内容,填写完后,单击“提交”,贴子就发表了,所以这里经过两个步骤:51Testing软件测试网g8]/q0Ql jP:GO%\&e
1、转到一个新增的页面,在Action里我们一般称为add,例如:51Testing软件测试网 [ gx G+J0_
public ActionForward add(ActionMapping mapping, ActionForm. form,51Testing软件测试网?p;w1Z6D
HttpServletRequest request, HttpServletResponse response)51Testing软件测试网/mK;T ^5WD
throws Exception {
|p4i%L,j0//这一句是输出调试信息,表示代码执行到这一段了
9\:Q4c]8f*@C0A0log.debug(":: action - subject add");51Testing软件测试网0]^]FLR4uE]+Q&}

xC(_XOiGUj0//your code here51Testing软件测试网p1SG(k%MqZ(u;v(c%M
51Testing软件测试网x3L'_1k C)u:nh
//这里保存Token值
;j-{ Z*Zz0um0saveToken(request);
$w YN |(B]051Testing软件测试网9{2Ji7{K1A4[ [Y
//跳转到add页面,在Structs-config.xml里面定义,例如,跳转到subjectAdd.jsp
e_"V cC KA/n0return mapping.findForward("add");51Testing软件测试网!P eK2Hqu
}
+y;@j(iD{~A LW051Testing软件测试网0j2A!G _)}.d J!_ P
2、在填写标题和内容后,选择 提交 ,会提交到insert方法,在insert方法里判断,是否重复提交了。
u/bL.?s(b;[0public ActionForward insert(ActionMapping mapping, ActionForm. form,51Testing软件测试网a0N5O3M-g-x)D
HttpServletRequest request, HttpServletResponse response){
V L0T:f jm8j0if (isTokenValid(request, true)) {51Testing软件测试网+I ptLb Vc*p+e(B
// 表单不是重复提交51Testing软件测试网,^b'tm5i
//这里是保存数据的代码
-b)Ye7a!F0@$|k0} else {
_:Q^A"~aI*{n.tE0//表单重复提交51Testing软件测试网r"| t"um
saveToken(request);51Testing软件测试网$u/x,x2f }
//其它的处理代码
X7D1\O'K(jS0}51Testing软件测试网*y }'KVEy*d4d0I
}51Testing软件测试网 v-XP ML'E^4KI/v
51Testing软件测试网 pp"D~H
下面更详细一点(注意,下面所有的代码使用全角括号):51Testing软件测试网K |^g_A ]v GG8A:b aI
1、你想发贴时,点击“我要发贴”链接的代码可以里这样的:
8{:^;@*MI'[H0〈html:link action="subject.do?method=add"〉我要发贴〈/html:link〉51Testing软件测试网0]f:W8@2B7?!~
subject.do 和 method 这些在struct-config.xml如何定义我就不说了,点击链接后,会执行subject.do的add方法,代码如上面说的,跳转到subjectAdd.jsp页面。页面的代码大概如下:51Testing软件测试网jC,E'_,fM3N
〈html:form. action="subjectForm.do?method=insert"〉
c`G3[Q&DO8o0〈html:text property="title" /〉51Testing软件测试网)}_&[:y7N
〈html:textarea property="content" /〉51Testing软件测试网%?1jkjR
〈html:submit property="发表" /〉
^/@g^5G5v!o0〈html:reset property="重填" /〉
*gImJnj#H:r+Lp0〈html:form〉
hO/RN3T;yD'R0如果你在add方法里加了“saveToken(request);”这一句,那在subjectAdd.jsp生成的页面上,会多一个隐藏字段,类似于这样〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,
o~3TR3Ni:I0
$C+hV:i3A7B2x02、点击发表后,表单提交到subjectForm.do里的insert方法后,你在insert方法里要将表单的数据插入到数据库中,如果没有进行重复提交的控制,那么每点击一次浏览器的刷新按钮,都会在数据库中插入一条相同的记录,增加下面的代码,你就可以控制用户的重复提交了。51Testing软件测试网$~1k!d9IIFKh
if (isTokenValid(request, true)) {51Testing软件测试网3DR,p#li;~ei0^r
// 表单不是重复提交51Testing软件测试网!v4d`S*F$y
//这里是保存数据的代码
%I_ n']U#z-l_0} else {
y#WT'`k0//表单重复提交51Testing软件测试网*M5L$y}3h&C e;]
saveToken(request);
v URGc3};B kIb YK0//其它的处理代码
+An-K9b!p"_#l)Mm"l,I0}51Testing软件测试网*D7c _T(e
注意,你必须在add方法里使用了saveToken(request),你才能在insert里判断,否则,你每次保存操作都是重复提交。
(yHzUg`Z N$u ~0记住一点,Struts在你每次访问Action的时候,都会产生一个令牌,保存在你的Session里面,如果你在Action里的函数里面,使用了saveToken(request);,那么这个令牌也会保存在这个Action所Forward到的jsp所生成的静态页面里。51Testing软件测试网Ak:Iz*T{d'}C
如果你在你Action的方法里使用了isTokenValid,那么Struts会将你从你的request里面去获取这个令牌值,然后和Session里的令牌值做比较,如果两者相等,就不是重复提交,如果不相等,就是重复提交了。51Testing软件测试网*_P lf W t
51Testing软件测试网*U1q[Y2C/z-@^!M
由于我们项目的所有Action都是继承自BaseDispatchAction这个类,所以我们基本上都是在这个类里面做了表单重复提交的控制,默认是控制add方法和insert方法,如果需要控制其它的方法,就自己手动写上面这些代码,否则是不需要手写的,控制的代码如下:51Testing软件测试网7MqJ%aR/T)j_8r#sfP
public abstract class BaseDispatchAction extends BaseAction {
Y6|?j1gzd0protected ActionForward perform(ActionMapping mapping, ActionForm. form,51Testing软件测试网I,e9T-kK Xp`FBmv
HttpServletRequest request, HttpServletResponse response)51Testing软件测试网nF8ps$f
throws Exception {
Z0W6M pk q0String parameter = mapping.getParameter();
hI"a"w{4w[0String name = request.getParameter(parameter);
,K&XO\5Eb*m-e0if (null == name) { //如果没有指定 method ,则默认为 list
6oqj ms!oF6C:H'Bw0name = "list";51Testing软件测试网~ty y5L s-iW;\
}51Testing软件测试网L%pF)V;eyJ A
51Testing软件测试网6YS#o SU;oSOj
if ("add".equals(name)) {51Testing软件测试网w9g6}c3PnE!u
if ("add".equals(name)) {
? r.BClj9{0saveToken(request);
YbTyTQ0r0}51Testing软件测试网8L m!x'F2]
} else if ("insert".equals(name)) {
Ol li%h [4eG{0if (!isTokenValid(request, true)) {
k0[7weMY&C0resetToken(request);51Testing软件测试网HEQqa
saveError(request, new ActionMessage("error.repeatSubmit"));51Testing软件测试网nx9dM}"?
log.error("重复提交!");
MD(d9ETWF"Qtq0return mapping.findForward("error");
d7Xz@r5T,i}d-H0}51Testing软件测试网!i Okg T}3G+k
}51Testing软件测试网A%a.f0m8[A
return dispatchMethod2(mapping, form, request, response, name);51Testing软件测试网#wQlG3[ d&S
}
'Kk$vmy{0}

TAG:

 

评分:0

我来说两句

日历

« 2024-04-09  
 123456
78910111213
14151617181920
21222324252627
282930    

数据统计

  • 访问量: 15497
  • 日志数: 11
  • 建立时间: 2008-05-21
  • 更新时间: 2009-08-13

RSS订阅

Open Toolbar