1.什么叫表单重复提交:
所谓表单重复提交,是指用户通过多次点击提交按钮或多次刷新表单提交页面等造成用户表单重复提交的现象
2.表单重复提交有哪些情况:
(1)用户在程序提交表单的时间段里多次提交表单
(2)重复刷新提交后的表单
(3)用户点击浏览器回退按钮,然后再次提交
3.如果解决表单重复提交:
(1)方法1:客户端防表单重复提交: 一般通过js代码防止第一种情况的发生,对于第二种和第三种的情况很难避免,并且稍微有经验的用户可以通过去掉页面js生成自己的html页面来访问服务器,这样客户端的防表单重复提交只能“防君子不能防小人”,并且可以增强用户体验感。
(2)方法2:服务器端session防表单重复提交:一般是利用令牌的原理来实现表单重复提交的,具体做法:
A. 对于用户每一次访问表单页面,均先经过CreateFormServlet的Servlet,其作用是在跳转表单页面之前,由BASE64Encoder类生成一个唯一的字符串,即表单号,并保存在session域中.
B.然后用户提交表单时,带着表单号,先验证客户端提交过来的表单号和session域中的表单号是否相同,如果不同则证明是重复提交,如果相同,则证明是第一次提交,并将表单号从session域中移除。
4.例子程序:
regist.jsp
[html] view plaincopyprint?
- <%@ page language="java"import="java.util.*"pageEncoding="utf-8"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
- <html>
- <scripttype="text/javascript">
- /*虽然客户端防表单重复提交,有很多漏洞,但是为了提高客户体验感,一把是客户端和服务器配合防表单重复提交,
- 客户端防重复提交的漏洞:1.客户复制源代码,去掉此js脚本,生成自己的html页面,然后提交
- 2.重复刷新提交后的表单
- 3.用户点击浏览器回退按钮,然后再次提交
- 客户端防重复提交的代码:
- 1.方法1:
- var isCommited =false;
- function dosubmit(){
- if(!isCommited){
- isCommited =
true; - return true;
- }else{
- return false;
- }
- }
- */
- //或者使"注册"按钮点击一次后不可用
- function dosubmit1(){
- var input = document.getElementById("submit");
- input.disabled="disabled";
- return true;
- }
- </script>
- <body>
- <formaction="/CookieAndSession/servlet/regist"method="post"onsubmit="return
dosubmit()"> - 用户名:<inputtype="text"name="username"value="aaa"><br/>
- <inputtype="hidden"name="c_token"value="${token}">
- <inputid="submit"type="submit"value="注册">
- </form>
- </body>
- </html>
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <script type="text/javascript"> /*虽然客户端防表单重复提交,有很多漏洞,但是为了提高客户体验感,一把是客户端和服务器配合防表单重复提交, 客户端防重复提交的漏洞:1.客户复制源代码,去掉此js脚本,生成自己的html页面,然后提交 2.重复刷新提交后的表单 3.用户点击浏览器回退按钮,然后再次提交 客户端防重复提交的代码: 1.方法1: var isCommited = false; function dosubmit(){ if(!isCommited){ isCommited = true; return true; }else{ return false; } } */ //或者使"注册"按钮点击一次后不可用 function dosubmit1(){ var input = document.getElementById("submit"); input.disabled="disabled"; return true; } </script> <body> <form action="/CookieAndSession/servlet/regist" method="post" onsubmit="return dosubmit()"> 用户名:<input type="text" name="username" value="aaa"><br/> <input type="hidden" name="c_token" value="${token}"> <input id="submit" type="submit" value="注册" > </form> </body> </html>
CreateFormServlet.java url-pattern: /servlet/createform
[java] view plaincopyprint?
- package edu.form;
- import java.io.IOException;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import java.util.Random;
- import javax.servlet.ServletException;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import sun.misc.BASE64Encoder;
- public class CreateFormServletextends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- //产生表单号
- String token = TokenProcessor.getInstance().generateToken();
- request.getSession().setAttribute("token", token);
- request.getRequestDispatcher("/regist.jsp").forward(request, response);
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
- class TokenProcessor{
- private static TokenProcessor token =new TokenProcessor();
- private TokenProcessor(){}
- public static TokenProcessor getInstance(){
- return token;
- }
- 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) {
- throw new RuntimeException(e);
- }
- }
- }
package edu.form; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Random; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import sun.misc.BASE64Encoder; public class CreateFormServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //产生表单号 String token = TokenProcessor.getInstance().generateToken(); request.getSession().setAttribute("token", token); request.getRequestDispatcher("/regist.jsp").forward(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } class TokenProcessor{ private static TokenProcessor token = new TokenProcessor(); private TokenProcessor(){} public static TokenProcessor getInstance(){ return token; } 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) { throw new RuntimeException(e); } } }
RegistServlet.java url-pattern: /servlet/regist
[java] view plaincopyprint?
- package edu.form;
- 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;
- public class RegistServletextends HttpServlet {
- public void doGet(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- response.setCharacterEncoding("utf-8");
- response.setContentType("text/html;charset=utf-8");
- PrintWriter out = response.getWriter();
- boolean isValid = tokenValidate(request);
- if(!isValid){
- out.print("<script>alert('请不要重复提交,程序正在处理!');</script>");
- return ;
- }
- out.println("正在向数据库注册用户信息!");
- }
- private boolean tokenValidate(HttpServletRequest request) {
- String c_token = request.getParameter("c_token");
- String s_token = (String) request.getSession().getAttribute("token");
- //防止用户自定义html
- if(c_token==null)
- return false;
- if (s_token==null)
- return false;
- if (!s_token.equals(c_token))
- return false;
- request.getSession().removeAttribute("token");
- return true;
- }
- public void doPost(HttpServletRequest request, HttpServletResponse response)
- throws ServletException, IOException {
- doGet(request, response);
- }
- }
package edu.form; 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; public class RegistServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter(); boolean isValid = tokenValidate(request); if(!isValid){ out.print("<script>alert('请不要重复提交,程序正在处理!');</script>"); return ; } out.println("正在向数据库注册用户信息!"); } private boolean tokenValidate(HttpServletRequest request) { String c_token = request.getParameter("c_token"); String s_token = (String) request.getSession().getAttribute("token"); //防止用户自定义html if(c_token==null) return false; if (s_token==null) return false; if (!s_token.equals(c_token)) return false; request.getSession().removeAttribute("token"); return true; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
程序不能直接进入regist.jsp页面,只能通过http://localhost:8080/CookieAndSession/servlet/createform进入CreateFormServlet.java通过此Servlet跳转至注册页面
此时http://localhost:8080/CookieAndSession/servlet/createform页面原代码如下:
这样就可以解决以上三种可能出现的表单重复提交问题!