防止表单重复提交
一:在客户端通过javascript防止表单重复提交
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>form.html</title> 5 <meta name="keywords" content="keyword1,keyword2,keyword3"> 6 <meta name="description" content="this is my page"> 7 <meta name="content-type" content="text/html; charset=UTF-8"> 8 <script type="text/javascript"> 9 var isCommitted = false; 10 function doSubmit() 11 { 12 if(isCommitted==false){ 13 isCommitted = true; 14 return true; 15 } 16 return false; 17 } 18 19 </script> 20 </head> 21 <body> 22 <form action="/test/servlet/FormServlet" method="post" onsubmit="return dosubmit()"> 23 用户名:<input type="text" name="username"/> 24 <br> 25 <input type="submit" value="提交"> 26 </form> 27 <br> 28 </body> 29 </html>
还可以提交过后将提交按钮变灰,无法提交
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>form.html</title> 5 <meta name="keywords" content="keyword1,keyword2,keyword3"> 6 <meta name="description" content="this is my page"> 7 <meta name="content-type" content="text/html; charset=UTF-8"> 8 <script type="text/javascript"> 9 function dosubmit(){ 10 var submit = document.getElementById("submit"); 11 submit.disabled= "disabled"; 12 return true; 13 14 } 15 </script> 16 </head> 17 <body> 18 <form action="/test/servlet/FormServlet" method="post" onsubmit="return dosubmit()"> 19 用户名:<input type="text" name="username"/> 20 <br> 21 <input type="submit" value="提交" id="submit"> 22 </form> 23 <br> 24 </body> 25 </html>
其实只在前台用javascript是禁不掉恶意用户的恶意操作的,必须在服务端处理。如用户单击”刷新”,或单击”后退”再次提交表单,将导致表单重复提交。但在服务端处理的同时,客户端也要处理,因为javascript最大的好处就是提升用户体验。
二利用session防止表单重复提交
表单页面由servlet程序生成,servlet为每次产生的表单页面分配一个唯一的随机标识号,并在FORM表单的一个隐藏字段中设置这个标识号,同时在当前用户的Session域中保存这个标识号。
当用户提交FORM表单时,负责处理表单提交的serlvet得到表单提交的标识号,并与session中存储的标识号比较,如果相同则处理表单提交,处理完后清除当前用户的Session域中存储的标识号。
在下列情况下,服务器程序将拒绝用户提交的表单请求:
存储Session域中的表单标识号与表单提交的标识号不同
当前用户的Session中不存在表单标识号
用户提交的表单数据中没有标识号字段
1 package cn.itcast.form; 2 3 import java.io.IOException; 4 import java.io.PrintWriter; 5 import java.security.MessageDigest; 6 import java.security.NoSuchAlgorithmException; 7 import java.util.Random; 8 9 import javax.servlet.ServletException; 10 import javax.servlet.http.HttpServlet; 11 import javax.servlet.http.HttpServletRequest; 12 import javax.servlet.http.HttpServletResponse; 13 14 import sun.misc.BASE64Encoder; 15 16 //负责产生表单 17 public class FormServlet extends HttpServlet { 18 19 public void doGet(HttpServletRequest request, HttpServletResponse response) 20 throws ServletException, IOException { 21 22 response.setContentType("text/html;charset=UTF-8"); 23 PrintWriter out = response.getWriter(); 24 25 String token = TokenProccessor.getInstance().makeToken(); 26 request.getSession().setAttribute("token", token); //在服务器端保存随机数 27 28 out.println("<form action='/day07/servlet/DoFormServlet' method='post'>"); 29 out.write("<input type='hidden' name='token' value='"+token+"'>"); 30 out.println("用户名:<input type='text' name='username'>"); 31 out.println("<input type='submit' value='提交'>"); 32 out.println("</form>"); 33 } 34 35 public void doPost(HttpServletRequest request, HttpServletResponse response) 36 throws ServletException, IOException { 37 38 doGet(request, response); 39 } 40 } 41 //工具类生成表单标识号 42 class TokenProccessor{ 43 44 /* 45 *单态设计模式(保证类的对象在内存中只有一个) 46 *1、把类的构造函数私有 47 *2、自己创建一个类的对象 48 *3、对外提供一个公共的方法,返回类的对象 49 * 50 */ 51 private TokenProccessor(){} 52 53 private static final TokenProccessor instance = new TokenProccessor(); 54 55 public static TokenProccessor getInstance(){ 56 return instance; 57 } 58 59 60 public String makeToken(){ //checkException 61 62 // 7346734837483 834u938493493849384 43434384 63 String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + ""; 64 //产生固定长度的随机数 65 //数据指纹 128位长 16个字节 md5 66 try { 67 MessageDigest md = MessageDigest.getInstance("md5"); 68 byte md5[] = md.digest(token.getBytes()); 69 70 //base64编码--任意二进制编码明文字符 71 BASE64Encoder encoder = new BASE64Encoder(); 72 return encoder.encode(md5); 73 74 } catch (NoSuchAlgorithmException e) { 75 throw new RuntimeException(e); 76 } 77 78 } 79 80 }
1 package cn.itcast.form; 2 3 import java.io.IOException; 4 import java.io.PrintWriter; 5 6 import javax.servlet.ServletException; 7 import javax.servlet.http.HttpServlet; 8 import javax.servlet.http.HttpServletRequest; 9 import javax.servlet.http.HttpServletResponse; 10 11 public class DoFormServlet extends HttpServlet { 12 13 public void doGet(HttpServletRequest request, HttpServletResponse response) 14 throws ServletException, IOException { 15 16 17 boolean b = isToken(request); //判断用户是否是重复提交 18 if(b==true){ 19 System.out.println("请不要重复提交"); 20 return; 21 } 22 23 request.getSession().removeAttribute("token"); 24 //移除session中的随机数 25 System.out.println("处理用户提交请求!!"); 26 27 } 28 29 private boolean isToken(HttpServletRequest request) { 30 31 String client_token = request.getParameter("token");//客户机带过来的随机数 32 //没有带随机数过来,说明是恶意用户用自己的form提交到服务端 33 if(client_token==null){ 34 return true; 35 } 36 //带了随机数过来,考察服务器保存的随机数 37 String server_token = (String) request.getSession().getAttribute("token"); 38 //服务器没有保存随机数,说明服务器处理过这个表单,处理表单后,将session中的这个随机数移除了 39 if(server_token==null){ 40 return true; 41 } 42 //用户带过来的随机数和服务器存的不一样,说明是恶意提交 43 if(!client_token.equals(server_token)){ 44 return true; 45 } 46 //正常提交 47 return false; 48 } 49 50 public void doPost(HttpServletRequest request, HttpServletResponse response) 51 throws ServletException, IOException { 52 53 doGet(request, response); 54 } 55 56 }