重复提交的情景:
一般控制重复提交主要是用在对数据库操作的控制上,比如插入、更新、删除等,由于更新、删除一般都是通过id来操作(例如: updateXXXById, removeXXXById),所以这类操作控制的意义不是很大(不排除个别现象),重复提交的控制也就主要是在插入时的控制了。
1.常见防重提交的方式主要有:js方式 验证码 令牌
2.这里主要介绍第三种:
其实从原理上来说,第二种跟第三种是一样的。都是通过生成随机数,存放于session,使得重复提交时获取到的数据与上一次发的数据不一致,从而不满足提交条件。
分三步:
1.初始化令牌值 AvoidSubmitFormAgain.initTokenProcessor(request, response); 生成token,并放置到session里
2.在页面上获取刚才生成的令牌值token
3.在后台对token进行校验:AvoidSubmitFormAgain.isTokenValid(request) true:可以提交 false:重复提交
4.清除当前的令牌值:AvoidSubmitFormAgain.removeTokenProecessor(request);
下面是具体代码:
令牌生成器:TokenProcessor.java
package com.sie.commons; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import sun.misc.BASE64Encoder; public class TokenProcessor { /* * 1.把构造函数私有 * 2.自己创建一个 * 3.对外暴露一个方法,允许获取上面创建的对象 * */ private static final TokenProcessor instance=new TokenProcessor(); private TokenProcessor(){} public static TokenProcessor getInstance() { return instance; } public String generateToken() { String token=System.currentTimeMillis()+new Random().nextInt()+""; try { MessageDigest md=MessageDigest.getInstance("md5"); byte[] md5=md.digest(token.getBytes()); //base64编码 BASE64Encoder encoder=new BASE64Encoder(); return encoder.encode(md5); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block throw new RuntimeException(e); } } }
第二个是我自己整合后一个令牌的操作工具AvoidSubmitFormAgain.java
package com.sie.commons; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 重复提交工具类 * @author lyq * */ public class AvoidSubmitFormAgain { /** * 初始化token * @param request * @param response * @param path * @throws ServletException * @throws IOException * @author lyq * @date:2014-4-25 下午4:05:27 * @version : */ public static void initTokenProcessor(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //产生随机数 TokenProcessor tp=TokenProcessor.getInstance(); String token=tp.generateToken(); request.getSession().setAttribute("token", token); } /** * 清除token * @param request * @author lyq * @date:2014-4-25 下午4:03:42 * @version : */ public static void removeTokenProecessor(HttpServletRequest request) { request.getSession().removeAttribute("token"); } /** * 对token的有效性进行判断 * @param request * @return * @author lyq * @date:2014-4-25 下午4:07:03 * @version : * 判断是否为重复提交 */ @SuppressWarnings("unused") public static boolean isTokenValid(HttpServletRequest request) { String client_token=request.getParameter("token"); if(client_token==null) { return false; } String server_token=(String)request.getSession().getAttribute("token"); if(server_token==null) { return false; } if(!client_token.equals(server_token)) { return false; } return true; } }
第三个是具体的使用逻辑,我们这里用于注册时的防重提交
public ActionForward user_registe(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws UserException { UserForm userform = (UserForm)form; //较验当前令牌与提交的是否相等,如果相同,则允许提交,否则,提交“重复提交”
if(!AvoidSubmitFormAgain.isTokenValid(request)) { userform.user_mess = "注册请求己提交,不要再重复提交,请重新注册"; return mapping.findForward("user_registe_resubmit_front"); } if(userservice.user_registe(userform.getUser())) { AvoidSubmitFormAgain.removeTokenProecessor(request); userform.user_mess = "用户注册成功,页面跳到登录页面"; request.setAttribute("userform",userform); return mapping.findForward("user_registe_success_front"); }
}