表单重复提交一般情况下有3种场景:
1> 网络延迟时,不断点击submit按钮
2> 表单提交后,用户点击刷新
3> 表单提交后,用户返回表单页面重新提交
针对这三种场景,在网上查阅各种方案后,感觉以下方案能够比较好的解决问题
第一种:
javascript方案 (只能用于第一种场景):
<form action = "doForm" id ="f" method = "post" > <input type = "text" name= "username" autocomplete = "off"/> <input type = "submit" id = "submit" value="提交" onclick = "checkSubmit()"> </form>
在js 中只要有标记变化就可以,可以使boolean,也可以是数值
var submitFlag = false; function checkSubmit(){ alert(submitFlag); if(!submitFlag){ submitFlag = true; return true; } return false; }
第二种:
在表单提交后,将按钮设为不可用(只适用于第一种场景):
function checkSubmit(){ document.getElementById("submit").disabled = "disabled"; return true; }
第三种:场景二 和 场景三 在客户端没办法解决,只能依赖服务器端解决,而此时就要用session了
具体方法:在服务器端生成一个标记号:Token(令牌)。发送到客户端,客户端将其保存在一个隐藏域中,表单提交时将隐藏域一起提交,在服务器端 对token进行比较,如果相同,说明是一次提交,处理完成后将session中的token删除;如果不同,说明是重复提交. 以下是相关源代码:
令牌生成类: package com.rcj.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import sun.misc.BASE64Encoder; /** * * @author Mars * Time:2017年9月29日 * */ public class TokenProccessor { //单例设计模式 private TokenProccessor() {}; private static TokenProccessor tp = new TokenProccessor(); public static TokenProccessor getInstance() { return tp; } /** * 生成Token * */ public String makeToken() { String token = (System.currentTimeMillis()+new Random().nextInt(999999999))+""; //数据指纹 MessageDigest md; try { md = MessageDigest.getInstance("md5"); byte[] md5 = md.digest(token.getBytes()); BASE64Encoder encoder = new BASE64Encoder(); return encoder.encode(md5); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } }
插入令牌servlet类 /** * * @author Mars * Time:2017年9月29日 * */ public class FormServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String token = TokenProccessor.getInstance().makeToken(); req.getSession().setAttribute("token", token); req.getRequestDispatcher("/resubmit.jsp").forward(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub doGet(req, resp); } }
form 表单: <%--使用隐藏域存储生成的token--%> <form action = "doForm" id ="f" method = "post" > <input type = "text" style="display:none" name = "token" value = "${token}"> <input type = "text" name= "username" autocomplete = "off"/> <input type = "submit" id = "submit" value="提交" onclick = "checkSubmit()"> </form> 数据提交处理servlet类:package com.rcj.servlet;
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * * @author Mars * Time:2017年9月29日 * */ public class DoFormServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf8"); boolean b = isRepeatSubmit(req); if(b == true) { System.out.println("请不要重复提交"); return ; } req.getSession().removeAttribute("token"); String username = req.getParameter("username"); System.out.println("数据库添加:"+username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub doGet(req, resp); } /** * 判断客户端提交上的令牌和服务器端是否一致 * true 用户重复提交了表单 * false 用户没有重复提交表单 */ private boolean isRepeatSubmit(HttpServletRequest request) { try { String client_token = request.getParameter("token"); if(client_token == null) { return true; } String server_token = request.getSession().getAttribute("token").toString(); if(request.getSession().getAttribute("token").toString() == null) { return true; } if(!client_token.equals(server_token)) { return true; } } catch (Exception e) { return true; } return false; } }
时间: 2024-11-11 06:38:28