客户端和服务器后退操作防止表单重复提交
表单重复提交的情况大体有两种:一种是网速卡,造成表单没有提交的假象,用户不停的点击提交。另一种则是用户的故意操作,后退或刷新页面然后重新提交。要做的就是防止这两种情况的表单重复提交。
防止第一种,我们可以在客户端添加操作防止现象发生,有两种方式,都是通过javascript实现的。第一种方式是在javascript中设置一个变量。初值为false,点击提交按钮之后将变量改为true,并且,在变量为false时按钮有提交作用。代码如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>ReapteForm.html</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript"> var competeSign = false; function check() { if(!competeSign) { competeSign = true; return competeSign; }else { alert("不能重复提交表单"); return false; } } </script> </head> <body style="background-color: white;"> <form action="/learnJS/servlet/RepeateFormServlet" method="post" onsubmit="return check()"> 留言内容:<br/> <textarea rows="10" cols="50"></textarea><br/> <input type="submit" value="提交"/> </form> </body></html><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript"> function dosubmit() { var input = document.getElementById("submit"); input.disabled = 'disabled'; return true; } </script> </head> <body style="background-color: white;"> <form action="/learnJS/servlet/RepeateFormServlet" method="post" onsubmit="return dosubmit();"> 留言内容:<br/> <textarea rows="10" cols="50"></textarea><br/> <input type="submit" id="submit" value="提交"/> </form> </body></html>再有就是在服务器端通过session方式防止表单的重复提交,这样只能防止后退操作,不能防止刷新操作的。思路是,在页面的表单中有一个隐藏项,每一个表单都有一个唯一的标号,同时session中设置一个属性域,通过标号和属性域的比对操作,确定表单是否为重复提交。页面代码为:<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><%@page import="com.you.learn.TokenProcessor"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head> <title>FormGenerate.jsp</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <script type="text/javascript"> function beformsubmit() { var p1 = document.getElementById("p1").value; if(p1==""){ alert("请输入内容!"); return false; } return true; } </script> </head> <body style="background-color: white;"> <% TokenProcessor.getInstance().saveToken(request); String token = (String)request.getSession().getAttribute(TokenProcessor.FORM_TOKEN_KEY); String name = TokenProcessor.FORM_TOKEN_KEY; System.out.println("html:" + token); %> <form action="/learnJS/servlet/FormDealServlet" method="post" onsubmit="return beformsubmit();"> <input type="hidden" name="<%=name %>" value="<%=token %>"> 字段1:<input type="text" name="p1" id="p1"/><br/> <input type="submit" value="提交"/> </form> </body></html>package com.you.servlet;import java.io.IOException;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.you.learn.TokenProcessor;public class FormDealServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();request.getParameter("TokenProcessor.FORM_TOKEN_KEY");TokenProcessor tokenProcessor = TokenProcessor.getInstance();if(!tokenProcessor.isTokenValid(request)) {out.println("这是重复或非法提交!");response.sendRedirect("/learnJS/FormGenerate.jsp");return;}String p1 = request.getParameter("p1");if(p1 == null || p1.trim().equals("")) {out.println("请输入内容!");}else {out.println("提交内容已被处理!");tokenProcessor.resetToken(request);}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}package com.you.learn;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import sun.misc.BASE64Encoder;public class TokenProcessor {private long previous;//上次生成表单标识号的时间值private static TokenProcessor instance = new TokenProcessor();public static String FORM_TOKEN_KEY = "FORM_TOKEN_KEY";private TokenProcessor() {}@SuppressWarnings("unused")public static TokenProcessor getInstance() {return instance;}/* * 验证请求消息中的标识号是否有效,如果验证请求消息中的标识号和 * 用户session域中的标识号相同,返回结果为true,否则返回false * */public synchronized boolean isTokenValid(HttpServletRequest request) {/* * 为避免session对象不存在时创建session对象, * 下面的语句不用request.getSession() */HttpSession session = request.getSession(false);if(session == null) {return false;}String saved = (String)request.getSession().getAttribute("FORM_TOKEN_KEY");System.out.println("Token:" + saved);if(saved == null) {return false;}String token = request.getParameter("FORM_TOKEN_KEY");System.out.println("Token+token:" + token);if(token == null) {return false;}System.out.println("equals:" + saved.equals(token));return saved.equals(token);}/** * 清除存储在当前用户session中的表单标识号 */public synchronized void resetToken(HttpServletRequest request) {HttpSession session = request.getSession(false);if(session == null) {return;}session.removeAttribute(FORM_TOKEN_KEY);}/** * 产生表单标识号,并将保存到当前用户的session中 */public synchronized void saveToken(HttpServletRequest request) {HttpSession session = request.getSession();try {byte[] id = session.getId().getBytes();long current = System.currentTimeMillis();if(current == previous) {current++;} previous = current;byte[] now = String.valueOf(current).getBytes();//获得随机数摘要(128个字节)MessageDigest md = MessageDigest.getInstance("MD5");md.update(id);md.update(now);//base64编码BASE64Encoder encoder = new BASE64Encoder();String token = encoder.encode(md.digest());session.setAttribute(FORM_TOKEN_KEY, token);System.out.println("java:" + session.getAttribute("FORM_TOKEN_KEY"));} catch (NoSuchAlgorithmException e) {e.printStackTrace();}}}