您的位置:首页 > 编程语言 > Java开发

java web项目防止表单重复提交的实现方案

2014-06-17 16:55 549 查看
当用户在表单中填写完信息,单击“提交”按钮后,可能会因为没有看到成功信息而再次单击“提交”按钮,从而导致在服务端接收到两条同样的信息,如果这个信息是要保存到数据库里的,那么就会出现两条相同的信息,而这往往往会引起数据库异常,对整个系统的稳定运行会产生致命的危害。在实际应用中,由于用户没有及时看到响应信息而导致的重复提交时有发生。响应不及时有可能是因为这个时段服务器的负载较大,又或者这个处理本身就是比较耗时的操作。
     有时候,即使响应及时,也有可能会出现重复提交的情况。服务器端的程序在处理完用户提交的信息后,调用了RequestDispatcher.forward()方法将用户的请求转发给成功页面,用户看到成功信息后,单了浏览器的“刷新”按钮,此时浏览器会再次提交用户先前输入的数据,这是因为调用了RequestDispatcher.forward()方法,浏览器所保留的URL是先前表单提交的URL,如果是采用了RequestDispatcher.sendRedircert()方法将客户端重定向到成功页面,就不会出现重复提交的问题了。

下面用客户端与服务器端令牌相结合的方式,防止用户重复提交表单。

废话少说,出代码

本示例项目文件结构如下图:

 



login.jsp页面代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<%@ page import="com.test.TokenProcessor" %>

<%@ page contentType="text/html; charset=UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html>

  <head>

    <title>防止表单重复提交</title>

    <script type="text/javascript">

     <!--

      var checkSubmitFlg=true;

      function checkSubmit(){

       if(true==checkSubmitFlg){

        document.theForm.btnSubmit.disable=true;

        document.theForm.submit();

        checkSubmitFlg=false;

       }else{

        alert("你已经提交 了表单,请不要重复提交!");

       }

      }

     //-->

    </script>

  </head>

 

  <body>

   <%

    TokenProcessor processor=TokenProcessor.getInstance();

    String token=processor.getToken(request);

    %>

 <form action="handler" name="theForm" method="post">

  <table>

   <tr>

    <td>用户名:</td>

    <td><input type="text" name="username"/></td>

   </tr>

   <tr>

    <td>邮件地址:</td>

    <td>

     <input type="text" name="email"/>

     <input type="hidden" name="ltai701" value="<%=token %>"/>

    </td>

   </tr>

   <tr>

    <td><input type="reset" value="重填"/></td>

    <td><input type="button" value="提交" name="btnSubmit" onclick="checkSubmit()"/></td>

   </tr>

  </table>

 </form>

  </body>

</html>

 

 

HandlerServlet代码如下:

package com.test;

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;

/**

 *

 * @author ltai701

 * @createTime 2009-08-01 12:35

 */

public class HandlerServlet extends HttpServlet {

 /**

  *

  */

 private static final long serialVersionUID = 1L;

 int count=0;

 protected void doGet(HttpServletRequest req, HttpServletResponse resp)

   throws ServletException, IOException {

  doPost(req, resp);

 }

 protected void doPost(HttpServletRequest req, HttpServletResponse resp)

   throws ServletException, IOException {

  resp.setContentType("text/html;charset=UTF-8");

  PrintWriter out=resp.getWriter();

  

  TokenProcessor processor=TokenProcessor.getInstance();

  if(processor.isTokenValid(req)){

   

  /*try{

   Thread.sleep(5000);

  }catch (InterruptedException e) {

   System.err.println(e);

   

  }*/

  

  System.out.println("submit:"+count);

  if(count%2==1) count=0;

  else count++;

  

  out.println("success");

  

  }else{

   processor.saveToken(req);

   out.println("你已经提交了表单,同一表单不能两次提交");

  }

  out.close();

 }

}

 

 

 

TokenProcessor代码如下:

package com.test;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpSession;

/**

 * TokenProcess类是一个单例类

 * @author ltai701

 *

 */

public class TokenProcessor {

 static final String TOKEN_KEY="ltai701";

 

 private static TokenProcessor instance=new TokenProcessor();

 

 /**

  * getInstance()方法得到单例类实例

  */

 public static TokenProcessor getInstance(){

  return instance;

 }

 

 /**

  * 最近一次生成令牌值的时间戳

  */

 private long previous;

 

 /**

  * 判断请求参数中的令牌值是否有效

  */

 public synchronized boolean isTokenValid(HttpServletRequest request){

  //得到请求的当前session对象

  HttpSession session=request.getSession(false);

  if(session==null){

   return false;

  }

  

  //从session中取出保存的令牌值

  String saved=(String)session.getAttribute(TOKEN_KEY);

  if(saved==null){

   return false;

  }

  

  //清除session中的令牌值

  resetToken(request);

  

  //得到请求参数中的令牌值

  String token=request.getParameter(TOKEN_KEY);

  if(token==null){

   return false;

  }

  

  return saved.equals(token);

 }

 

 /**

  * 清除session中的令牌值

  */

 public synchronized void resetToken(HttpServletRequest request){

  HttpSession session=request.getSession(false);

  if(session==null){

   return;

  }

  session.removeAttribute(TOKEN_KEY);

 }

 /**

  * 产生一个新的令牌值 ,保存到session中

  * 如果当前sesison不存在,则创建一个新的的session

  */

 public synchronized void saveToken(HttpServletRequest request){

  HttpSession session=request.getSession(false);

  String token=generateToken(request);

  if(token!=null){

   session.setAttribute(TOKEN_KEY, token);

  }

 }

 

 /**

  * 根据用户会话id和当前系统时间生成一个唯一的令牌

  */

 public synchronized String  generateToken(HttpServletRequest request){

  HttpSession session =request.getSession(false);

  try{

   byte id[]=session.getId().getBytes();

   long current=System.currentTimeMillis();

   if(current==previous){

    current++;

    

   }

   previous=current;

   byte now[]=new Long(current).toString().getBytes();

   MessageDigest md=MessageDigest.getInstance("MD5");

   md.update(id);

   md.update(now);

   return toHex(md.digest());

  }catch (NoSuchAlgorithmException e) {

   // TODO: handle exception

   e.printStackTrace();

   return null;

  }

 }

 

 /**

  * 将一个字节数组转换一个十六进制数字的字符串

  * @param buffer

  * @return

  */

 private String toHex(byte buffer[]){

  StringBuffer sb=new StringBuffer(buffer.length*2);

  for(int i=0;i<buffer.length;i++){

   sb.append(Character.forDigit((buffer[i]&0xf0)>>4, 16));

   sb.append(Character.forDigit(buffer[i]&0x0f, 16));

  }

  

  return sb.toString();

 }

 

 /**

  * 从Session中得到令牌值,如果Session中没有令牌值 ,则生成一个新的令牌值

  */

 public synchronized String getToken(HttpServletRequest request){

  HttpSession session=request.getSession(false);

  if(null==session)

   return null;

  

  String token=(String)session.getAttribute(TOKEN_KEY);

  

  if(null==token){

   token=generateToken(request);

   if(token!=null){

    session.setAttribute(TOKEN_KEY,token);

    return token;

   }else

    return null;

  }else

   return token;

 }

}

 

 

web.xml配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"

 xmlns="http://java.sun.com/xml/ns/j2ee"

 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee

 http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

 

 <servlet>

  <servlet-name>HanderServlet</servlet-name>

  <servlet-class>com.test.HandlerServlet</servlet-class>

 </servlet>

 <servlet-mapping>

  <servlet-name>HanderServlet</servlet-name>

  <url-pattern>/handler</url-pattern>

 </servlet-mapping>

  <welcome-file-list>

    <welcome-file>index.jsp</welcome-file>

    <welcome-file>login.jsp</welcome-file>

  </welcome-file-list>

</web-app>

 

 

运行效果图如下:

等待服务器端响应时重复按“提交”按钮





 

提交完成后再刷新浏览器或者按回退键再按前进键,则有





希望此文章能帮助到有需要的人,多谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java web 表单