防止表单重复提交
一、原理
表单重复提交的三种情况:
1.在表单没有到达目标页面前,对请求按钮点击n次。浏览器会将所有点击的请求排成一个队列,先进先出。
2.在表单提交到达目标页面后,刷新目标页面。
3.在表单提交后到达目标页面后,点击后退,再次提交。
使用JS和重定向只能解决部分情况。
使用token来解决:表单和Session域同时维护一个token唯一值。
在加载页面的时候,同时向session域中和表单隐藏域中放入相同的token。
<% String tokenStr = System.currentTimeMillis() + "-" + session.getId(); session.setAttribute("token", tokenStr); %> <form action="/Session/SessionServlet" method="post"> <input type="hidden" name="tokenParam" value="<%=tokenStr %>" /> <input type="submit" value="submit"> </form>
到达目标方法后:
判断表单提交的token值是否和session域中的值相等,若相等,则删除session域中的token值,到达目标页面。
若重复提交,则在seesion域中找不到对应的token值,跳转到错误页面,给出提示。
String tokenParam = req.getParameter("tokenParam"); HttpSession session = req.getSession(); Object token = session.getAttribute("token"); if(tokenParam == null || !tokenParam.equals(token)) { System.out.println("重复提交!!"); return ; } session.removeAttribute("token"); req.getRequestDispatcher("/target.jsp").forward(req, resp);
二、Struts2
(1)使用 <s:token/> 和 org.apache.struts2.interceptor.TokenInterceptor 或 org.apache.struts2.interceptor.TokenSessionStoreInterceptor 来进行防止表单重复提交。
(2)<s:token/>
在页面中使用 Struts2 标签 <s:token/> 会被解析为:
<input type="hidden" name="struts.token.name" value="token"> <input type="hidden" name="token" value="NUV93RXS5OQQVWL1Y1BTKHPPFK3L4UAX">
(3)拦截器的配置
默认拦截器栈 defaultStack,没有配置 org.apache.struts2.interceptor.TokenInterceptor 或是 org.apache.struts2.interceptor.TokenSessionStoreInterceptor。需要手动加入。
<action name="token" class="com.nucsoft.struts.token.TokenAction"> <interceptor-ref name="token"/> <interceptor-ref name="defaultStack"/> <result>/success.jsp</result> <result name="invalid.token">/form.jsp</result> </action> <action name="tokenSession" class="com.nucsoft.struts.token.TokenAction" method="tokenSession"> <interceptor-ref name="tokenSession"/> <interceptor-ref name="defaultStack"/> <result>/success.jsp</result> </action>
(4)org.apache.struts2.interceptor.TokenInterceptor 和 org.apache.struts2.interceptor.TokenSessionStoreInterceptor 的区别
TokenInterceptor :需要配置一个 name="invalid.token" 的一个 result,表单重复提交时,给出提示。提示信息会存放到 actionError 中,可以通过标签 s:actionerror 来获取信息。
也可以在国际化资源文件中对信息实现定制(指定国际化资源文件基名):
如:
i18n_zh_CN.properties
struts.messages.invalid.token=\u8bf7\u4e0d\u8981\u91cd\u590d\u63d0\u4ea4\u8868\u5355(请不要重复提交表单)
TokenSessionStoreInterceptor :不需要配置别的 result ,采取是一种静默模式,表单重复提交时,不会给出提示,但是实际上目标方法只会被执行一次。
未写完,待续