`

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>

 

分享到:
评论

相关推荐

    自定义注解解决API接口幂等设计防止表单重复提交(生成token存放到redis中)

    为了解决这一问题,我们可以采用自定义注解结合Redis来实现一个防止表单重复提交的解决方案。 首先,让我们理解自定义注解的核心思想。注解是一种元数据,它提供了在代码中添加信息的方式,这些信息可以被编译器或...

    浅谈利用Session防止表单重复提交

    浅谈利用Session防止表单重复提交 Session是Web应用程序中的一种机制,用于存储用户的会话信息。在Web应用程序中,表单重复...使用Session结合UUID可以有效地防止表单重复提交,提高了Web应用程序的安全性和可靠性。

    struts2 防止表单重复提交的例子

    在Web开发中,表单重复提交是一个常见的问题,它可能导致数据的不一致性或者服务端处理逻辑的混乱。Struts2框架提供了一种解决方案,即使用Token机制来防止表单的重复提交。以下是对这个主题的详细说明: 1. **表单...

    自定义标签防止表单重复提交

    在Web开发中,表单重复提交是一个常见的问题,可能导致数据不一致或服务器资源浪费。本文将深入探讨如何通过自定义标签来防止表单的重复提交,以确保数据的一致性和系统的稳定性。 首先,理解表单重复提交的问题。...

    [Jsp]防止页面表单重复提交的解决方法

    二、防止表单重复提交的方法 1. **使用JavaScript禁用提交按钮** 在表单提交时,可以使用JavaScript来禁用提交按钮,以防止用户连续点击。例如: ```javascript function disableSubmit() { document....

    struts2防止表单重复提交--重定向

    下面将详细解释Struts2如何通过重定向来防止表单重复提交。 首先,理解表单重复提交的场景:用户在提交表单后,由于网络延迟或其他原因,可能会无意中多次点击提交按钮。如果服务器没有处理这些重复请求,那么相同...

    jsp项目放置表单重复提交

    防止表单重复提交的方法通常有两种:客户端解决方案和服务器端解决方案。 1. 客户端解决方案: - JavaScript禁用提交按钮:在提交表单时,可以通过JavaScript立即禁用提交按钮,避免用户连续点击。 - 使用Ajax...

    struts数据回显、模型驱动、防止表单重复提交

    综上所述,理解并掌握Struts框架中的数据回显、模型驱动以及防止表单重复提交的原理和实现方法,对于提升Java Web应用的开发效率和质量具有重要意义。在实际开发中,应灵活运用这些技术,以满足项目需求,打造稳定...

    SSH笔记-数据验证、上传下载、避免表单重复提交、自定义拦截器

    总结来说,这个SSH笔记涵盖了Web开发中的关键知识点,包括数据验证确保输入合法性,文件上传下载提供交互性,防止表单重复提交保护数据一致性,以及自定义拦截器增强应用的功能和灵活性。掌握这些技能,将有助于提升...

    redis专栏 002 springboot redis 防止表单重复提交

    使用Redis和Spring Boot来防止表单重复提交的基本思路是:在接收到表单提交请求后,生成一个唯一的请求标识(例如,基于UUID),并将其作为键存储到Redis中,设置一个适当的过期时间。当服务器接收到新的请求时,会...

    Java怎样防止重复提交

    在Java Web开发中,防止重复提交是一个重要的议题,尤其是在大型分布式应用中,因为这可能导致服务器资源的过度消耗,甚至引发系统性能问题。以下是一些防止重复提交的常见策略和技术: 1. **令牌(Token)机制**:...

    Java表单重复提交的避免方法

    Java表单重复提交是Web开发中常见的问题,可能导致数据不一致和系统异常。要理解这个问题,首先要明白表单提交的基本流程:用户打开表单页面,填写信息,然后点击提交按钮,浏览器向服务器发送请求,服务器处理请求...

    sturts2防止表单的重复提交 token

    在Web开发中,防止表单重复提交是一个常见的需求,尤其是在处理敏感数据或者执行不可逆操作时。Struts2为解决这个问题引入了Token机制,确保每个表单提交只被处理一次,避免了由于用户意外刷新页面导致的重复请求。 ...

    防止页面刷新重复提交的方法.

    防止页面刷新重复提交是 Web 开发中常见的问题,多次提交表单可能会导致不必要的数据重复录入、服务器压力增大等问题。下面我们将详细介绍防止页面刷新重复提交的方法。 一、验证码方法 验证码方法是防止页面刷新...

    Struts2解决表单重复提交

    Struts2作为一个流行的Java Web框架,为解决表单重复提交提供了多种方法。 首先,关于表单重复提交的原因,有以下几点: 1. 服务器或网络延迟导致用户多次点击提交按钮。 2. 用户在表单提交后刷新浏览器页面。 ...

    Struts2防止表单重复提交示例

    在Struts2中防止表单重复提交的过程主要包括以下几个步骤: 1. **生成Token**:当用户发起表单请求时,服务器会生成一个唯一的Token并将其存储在服务器的会话(Session)中,同时将这个Token作为隐藏字段放入到HTML...

    基于springboot实现表单重复提交.docx

    表单重复提交是指在一次请求完成之前防止重复提交,解决表单重复提交有多种形式,以下以 Aop+自定义注解+Redis 为例来介绍。 解决方案的详细流程 1. 当页面加载时,前端请求后台,后台生成 token 缓存到 Redis ...

    struts+token机制解决表单重复提交

    Struts是Java Web开发中的一款流行MVC框架,它简化了构建基于JSP的Web应用程序的过程。然而,Web应用在处理表单提交...通过对这些代码的学习和研究,开发者能够熟练地在自己的项目中应用Struts+Token防止表单重复提交。

    Struts之Token解决表单那重复提交

    综上所述,Struts框架通过Token机制有效地解决了表单重复提交的问题,提供了更健壮的Web应用安全性。正确理解和应用这一机制对于Java Web开发者来说至关重要。在实际项目中,结合其他防御策略,如CSRF防护,可以...

Global site tag (gtag.js) - Google Analytics