Struts的TokenQo牌)机制能够很好的解册单重复提交的问题Q基本原理是Q服务器端在处理到达的请求之前,会将h中包含的令牌?/i>? 保存在当前用户会话中的o牌D行比较,看是否匹配。在处理完该h后,且在{复发送给客户端之前,会产生一个新的o牌,该o牌除传给客户端以外,也会 用户会话中保存的旧的o牌进行替换。这样如果用户回退到刚才的提交面q再ơ提交的话,客户端传q来的o牌就和服务器端的令牌不一_从而有效地防止? 重复提交的发生?/p>
q时其实也就是两点,W一Q你需要在h中有q个令牌|h中的令牌值如何保存,其实和我们qx在页面中保存一些信息是一LQ通过隐藏字段? 保存Q保存的形式如: 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,q个value是TokenProcessorcM? generateToken()获得的,是根据当前用Lsession id和当前时间的long值来计算的。第二:在客L提交后,我们要根据判断在h中包含的值是否和服务器的令牌一_因ؓ服务器每ơ提交都会生成新? TokenQ所以,如果是重复提交,客户端的Token值和服务器端的Token值就会不一致。下面就以在数据库中插入一条数据来说明如何防止重复提交?/p>
在Action中的addҎ中,我们需要将Token值明的要求保存在页面中Q只需增加一条语句:saveToken(request);Q如下所C:
public ActionForward add(ActionMapping mapping,
ActionForm form,
HttpServletRequest request, HttpServletResponse
response)
//前面的处理省?br>saveToken(request);
return
mapping.findForward("add");
}在Action的insertҎ中,我们Ҏ表单中的Tokeng服务器端的Token值比较,如下所C:
public ActionForward insert(ActionMapping mapping, ActionForm
form,
HttpServletRequest request, HttpServletResponse response)
if
(isTokenValid(request, true)) {
// 表单不是重复提交
//q里是保存数据的代码
} else
{
//表单重复提交
saveToken(request);
//其它的处理代?br>}
}
其实使用h很简单,举个最单、最需要用这个的例子Q?br>一般控刉复提交主要是用在Ҏ据库操作的控制上Q比如插入、更新、删除等Q由于更新、删除一般都是通过id来操作(例如QupdateXXXById,
removeXXXByIdQ,所以这cL作控制的意义不是很大Q不排除个别现象Q,重复提交的控制也׃要是在插入时的控制了?br>
先说一下,我们目前所做项目的情况Q?br>?
前的目是用StrutsQSpringQIbatisQ页面用jstlQStruts复杂View层,Spring在Service层提供事务控Ӟ
Ibatis是用来代替JDBCQ所有页面的讉K都不是直接访问jspQ而是讉KStructs的ActionQ再由Action来ForwardC?
JspQ所有针Ҏ据库的操作,比如取数据或修改数据Q都是在Action里面完成Q所有的Action一般都l承
BaseDispatchActionQ这个是自己建立的类Q目的是为所有的Action做一些统一的控Ӟ在Struts层,对于一个功能,我们一般分
Z个ActionQ一个Action里的功能是不需要调用Struts的验证功能的Q常见的Ҏ名称有add,edit,remove,view,
listQ,另一个是需要调用Struts的验证功能的Q常见的Ҏ名称有insert,updateQ?br>
拿论坛发脓来说吧,论坛发脓首先需要蟩转到一个页面,你可以填写帖子的主题和内容,填写完后Q单几Z提交”,贴子发表了Q所以这里经q两个步骤:
1、{C个新增的面Q在Action里我们一般称为addQ例如:
public
ActionForward add(ActionMapping mapping, ActionForm
form,
HttpServletRequest request, HttpServletResponse
response)
throws Exception
{
//q一句是输出调试信息Q表CZ码执行到q一D了
log.debug(":: action - subject
add");
//your code here
//q里保存Token?br> saveToken(request);
//跌{到add面Q在Structs-config.xml里面定义Q例如,跌{到subjectAdd.jsp
return
mapping.findForward("add");
}
2、在填写标题和内容后Q选择 提交 Q会提交到insertҎQ在insertҎ里判断,是否重复提交了?br>public ActionForward
insert(ActionMapping mapping, ActionForm form,
HttpServletRequest request,
HttpServletResponse response){
if (isTokenValid(request, true)) {
//
表单不是重复提交
//q里是保存数据的代码
} else
{
//表单重复提交
saveToken(request);
//其它的处理代?br>}
}
下面更详l一点(注意Q下面所有的代码使用全角括号Q:
1、你惛_贴时Q点几Z我要发贴”链接的代码可以里这LQ?br>〈html:link
action="subject.do?method=add"〉我要发贴?html:link?br>subject.do ?method
q些在struct-config.xml如何定义我就不说了,点击链接后,会执行subject.do的addҎQ代码如上面说的Q蟩转到subjectAdd.jsp面。页面的代码大概如下Q?br>〈html:form
action="subjectForm.do?method=insert"?br> 〈html:text property="title"
/?br> 〈html:textarea property="content" /?br> 〈html:submit property="发表"
/?br> 〈html:reset property="重填"
/?br>〈html:form?br>如果你在addҎ里加了“saveToken(request);”这一句,那在subjectAdd.jsp生成的页面上Q会多一个隐藏字D,cM于这栗input
type="hidden" name="org.apache.struts.taglib.html.TOKEN"
value="6aa35341f25184fd996c4c918255c3ae"〉,
2、点d表后Q表单提交到subjectForm.do里的insertҎ后,你在insertҎ里要表单的数据插入到数据库中,如果没有
q行重复提交的控Ӟ那么每点Mơ浏览器的刷新按钮,都会在数据库中插入一条相同的记录Q增加下面的代码Q你可以控制用L重复提交了?br>if
(isTokenValid(request, true)) {
// 表单不是重复提交
//q里是保存数据的代码
} else
{
//表单重复提交
saveToken(request);
//其它的处理代?br>}
注意Q你必须在addҎ里用了saveToken(request)Q你才能在insert里判断,否则Q你每次保存操作都是重复提交?br>?
住一点,Struts在你每次讉KAction的时候,都会产生一个o牌,保存在你的Session里面Q如果你在Action里的函数里面Q用了
saveToken(request);Q那么这个o牌也会保存在q个Action所Forward到的jsp所生成的静态页面里?br>如果你在你Action的方法里使用了isTokenValidQ那么Struts会将你从你的request里面去获取这个o牌|然后和Session里的令牌值做比较Q如果两者相{,׃是重复提交,如果不相{,是重复提交了?br>
?
于我们项目的所有Action都是l承自BaseDispatchActionq个c,所以我们基本上都是在这个类里面做了表单重复提交的控Ӟ默认是控
制addҎ和insertҎQ如果需要控制其它的ҎQ就自己手动写上面这些代码,否则是不需要手写的Q控制的代码如下Q?br>public
abstract class BaseDispatchAction extends BaseAction {
protected
ActionForward perform(ActionMapping mapping, ActionForm
form,
HttpServletRequest request, HttpServletResponse
response)
throws Exception {
String parameter =
mapping.getParameter();
String name =
request.getParameter(parameter);
if (null == name) { //如果没有指定 method Q则默认?
list
name = "list";
}
if
("add".equals(name)) {
if ("add".equals(name))
{
saveToken(request);
}
} else if
("insert".equals(name)) {
if (!isTokenValid(request, true))
{
resetToken(request);
saveError(request,
new
ActionMessage("error.repeatSubmit"));
log.error("重复提交Q?);
return
mapping.findForward("error");
}
}
return
dispatchMethod2(mapping, form, request, response,
name);
}
}