本文转载自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();
}
}