`

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

    博客分类:
  • j2ee
阅读更多

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

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

    主要介绍了JAVA防止重复提交Web表单的方法,涉及Java针对表单的相关处理技巧,具有一定参考借鉴价值,需要的朋友可以参考下

    JSP使用自定义标签防止表单重复提交的方法

    本文实例讲述了JSP使用自定义标签防止表单重复提交的方法。分享给大家供大家参考。具体如下: 1. 编写servelt: package cn.itcast.apsliyuan.web.servlet; import java.io.IOException; import javax.servlet....

    JAVA高并发高性能高可用高扩展架构视频教程

    Java-Base64算法_创新_防止表单重复提交 JAVA企业级基础课题(HashMap那些事) 企业架构师必备技能(JAVA核心技术反射) JavaWeb之基础(手写实现Tomcat服务器) java多线程编程 纯手写实现SpringIOC实现过程 JEE企业级...

    百度地图开发java源码-TypicalWebProject:一个典型的JavaWeb项目

    令牌机制防止表单重复提交。 注册表单的JS验证、Ajax用户名唯一性验证等等。 开发环境: jdk1.8+Tomcat 9+Mysql 5.7+Eclipse(本人用Oracle也做了一版,上传的程序是用的Mysql版) 项目功能模块: 用户注册、登录、...

    达内java培训目录

    Struts2 Struts2核心控制流程、Ognl、Action、Interceptor、Result、FreeMarker、Struts2标记库、Struts2扩展、Struts2应用技巧(输入验证、消息国际化、文件上传和下载、防止重复提交等)。 熟练掌握Struts2核心...

    深入浅出Struts2

    Struts 2 是Java Web 应用首选的MVC 框架。本书对Struts 2 的工作机理进行了透彻的阐述。书中介绍了如何利用...第15章 防止重复提交 第16章 调试与性能分析 第17章 进度条 第18章 定制拦截器 第19章... [显示全部]

    structs程序设计从入门到精通word文档

    9.3 Struts令牌机制,防止重复提交 13 9.4 StrutsAction单态陷阱,请谨慎使用全局变量 13 9.5 Struts异常处理 13 第10章 struts中使用国际化(i18n) 13 10.1 struts国际化程序尝试 13 10.1 Java对i18n的支持 14 10.1...

    ASP200问.EXE

    129.如何防止表单在网站外被提交 130.如何解决表单发送数据小于100KB的问题 131.如何在表单中动态添加文本框 132.如何解决下拉列表出现选项重复的问题 133.如何实现在下拉列表中输入文字 134.如何实现级联下拉列表 ...

    Struts2 in action中文版

    15.3 使用令牌防止表单重复提交 313 15.3.1 使用s:token/表单标签 313 15.3.2 令牌拦截器规则的例外 314 15.4 自动显示等待页面 316 15.5 完成CRUD操作的一个动作 317 15.5.1 CRUD 317 15.5.2 拦截器和接口 318 ...

    asp.net知识库

    SubmitOncePage:解决刷新页面造成的数据重复提交问题 SharpRewriter:javascript + xml技术利用#实现url重定向 采用XHTML和CSS设计可重用可换肤的WEB站点 asp.net的网址重定向方法的比较:面向搜索引擎友好 也谈 ...

    Struts2入门教程(全新完整版)

    5. TokenInterceptor防止表单重复提交。 34 6.使用拦截器实现权限验证 35 7.拦截器中的注解 37 8.使用PreResultListener实现回调 39 六、使用标签 40 1.基础表单标签 40 2.单选按钮和复选框: 41 3.三种方式实现下拉...

    JavaScript完全自学宝典 源代码

    7.6.html 防止onresize事件重复执行。 7.7.html onerror事件相关处理。 7.8.html onsubmit事件使用方法。 7.9.html 失去焦点时检验文本框的值。 7.10.html 获得焦点时文本框样式更改。 img.JPG...

    深入浅出Struts 2 .pdf(原书扫描版) part 1

    第15章 防止重复提交 252 15.1 标记管理 252 15.2 使用Token拦截器 253 15.3 使用Token Session拦截器 256 15.4 小结 257 第16章 调试与性能分析 258 16.1 debug标签 258 16.2 Debugging拦截器 259 16.3 性能分析 ...

    深入浅出Struts2(附源码)

    第15章防止重复提交 252 15.1 标记管理 252 15.2 使用Token拦截器 253 15.3 使用Token Session拦截器 256 15.4 小结 257 第16章调试与性能分析 258 16.1 debug标签 258 16.2 Debugging拦截器 259 16.3 性能...

Global site tag (gtag.js) - Google Analytics