java web项目防止表单重复提交

 当用户在表单中填写完信息,单击“提交”按钮后,可能会因为没有看到成功信息而再次单击“提交”按钮,从而导致在服务端接收到两条同样的信息,如果这个信息是要保存到数据库里的,那么就会出现两条相同的信息,而这往往往会引起数据库异常,对整个系统的稳定运行会产生致命的危害。在实际应用中,由于用户没有及时看到响应信息而导致的重复提交时有发生。响应不及时有可能是因为这个时段服务器的负载较大,又或者这个处理本身就是比较耗时的操作。

     有时候,即使响应及时,也有可能会出现重复提交的情况。服务器端的程序在处理完用户提交的信息后,调用了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" οnclick="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>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值