本文转载自http://www.oschina.net/code/snippet_78857_6953
这里分享的代码使用 filter 实现,利用 token 机制来防止重复提交。
当然要使用这个工具,首先你要在你项目的web.xml中配置好下面两个filter。其次,还需要此功能的 jsp 页面中加上这一段:
String clientToken = StringUtil.getToken(request);
还有,form中需要加上:
<input name="clientToken" type="hidden" value="<%=clientToken %>" />
这段代码的好处有二:
1> 解决了你的重复提交问题,
2> 做到了将 token 的功能和你后端的业务逻辑的解耦; 当然前端没有解耦。
~~~~~~仔细分析了一下,发现事实上, 做不到在前端解耦,因为 struts 也要求你在需要token功能的前端页面中加上<s: token /> 。这和web应用模型有关 -- 不能区分重复提交的request和正常提交的request,如果能, 也不需要这里的 token了, O(∩_∩)O~
下面来仔细的分析一把:
首先, 为什么F5会产生重复提交呢? 其实和提交后的request处理方式有关,如果你完成了数据处理后,使用了如下方式来跳转:
req.getRequestDispatcher(url).forward(req, resp);
就会在你F5的时候重复提交,因为这种跳转是在server端完成的,虽然跳转到了新的页面,但是并没有告知浏览器,于是当你F5的时候,再次带着你上次提交的所有的参数,提交到了相同的资源(通常是用来处理数据的逻辑代码,例如action),导致产生了重复提交。
那么 如何才能避免重复提交呢? 其实就一句话,帮助你的 action等处理单元 区分出重复提交的request, 我们这里使用token来给request打上标签^_^。如果我们发现那个request的标签过期了,那么我们就不做数据处理(例如添加一条评论等)具体做法:
1> 在提交之前准备好token,并更新server token (放在session中) 和 client token (放在页面中)
String clientToken = StringUtil.getToken(request);
同时,form中: <input name="clientToken" type="hidden" value="<%=clientToken %>" />
2>在有提交时进行检查:
在 TokenFilter中实现的。具体见代码。
UriRecFilter.java, 用来记录提交前的url
package com.tding.myject.filter; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import static com.tding.myject.MyJectConstants.REQUEST_SOURCE_URL; public class UriRecFilter implements Filter { @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; request.getSession().setAttribute(REQUEST_SOURCE_URL, request.getRequestURI()); chain.doFilter(req, resp); } @Override public void init(FilterConfig arg0) throws ServletException { // TODO Auto-generated method stub System.out.println("init UriRecFilter ..."); } }
TokenFilter.java, 实现令牌的分析处理
package com.tding.myject.filter; import java.io.IOException; import javax.servlet.*; import javax.servlet.http.*; import static com.tding.myject.MyJectConstants.REQUEST_SOURCE_URL; public class TokenFilter implements Filter { private HttpServletRequest request; private HttpServletResponse response; @Override public void destroy() { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub request = (HttpServletRequest) req; response = (HttpServletResponse) resp; if (isTokenOverdue(request)) { // 执行到此处说明令牌过期,即判断为重复提交 request.getRequestDispatcher(getRequestSourceUri(request)).forward( request, response); } else { chain.doFilter(req, resp); } } @Override public void init(FilterConfig arg0) throws ServletException { System.out.println("init TokenFilter ..."); } private boolean isTokenOverdue(HttpServletRequest req) { HttpSession session = req.getSession(); String st = (String) session.getAttribute("token"); // System.out.println("session Token: " + st); String ct = req.getParameter("clientToken"); return st != null && !st.equals(ct); } private String getRequestSourceUri(HttpServletRequest req) { String rUri = (String) req.getSession() .getAttribute(REQUEST_SOURCE_URL); if (rUri == null) rUri = "/"; /** * 最好不要放到这里,因为可能很多地方需要用到。 */ int clength = req.getContextPath().length(); return clength > 1 ? rUri.substring(clength + 1) : ""; } }
StringUtil.java, 工具类
package com.tding.myject.util; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public class StringUtil { public static String getParameter(HttpServletRequest req,String paraName) { String param = null; try { param = new String(req.getParameter(paraName).getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { param = null; e.printStackTrace(); } return param; } public static String getToken(HttpServletRequest req) { HttpSession sessions = req.getSession(); String token = StringUtil.generateToken(req); req.setAttribute("clientToken", token); sessions.setAttribute("token", token); return token; } public static String generateToken(HttpServletRequest req) { return req.getSession().getId() + System.currentTimeMillis(); } }