当用户在表单中填写完信息,单击“提交”按钮后,可能会因为没有看到成功信息而再次单击“提交”按钮,从而导致在服务端接收到两条同样的信息,如果这个信息是要保存到数据库里的,那么就会出现两条相同的信息,而这往往往会引起数据库异常,对整个系统的稳定运行会产生致命的危害。在实际应用中,由于用户没有及时看到响应信息而导致的重复提交时有发生。响应不及时有可能是因为这个时段服务器的负载较大,又或者这个处理本身就是比较耗时的操作。
有时候,即使响应及时,也有可能会出现重复提交的情况。服务器端的程序在处理完用户提交的信息后,调用了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>
相关推荐
为了解决这一问题,我们可以采用自定义注解结合Redis来实现一个防止表单重复提交的解决方案。 首先,让我们理解自定义注解的核心思想。注解是一种元数据,它提供了在代码中添加信息的方式,这些信息可以被编译器或...
浅谈利用Session防止表单重复提交 Session是Web应用程序中的一种机制,用于存储用户的会话信息。在Web应用程序中,表单重复...使用Session结合UUID可以有效地防止表单重复提交,提高了Web应用程序的安全性和可靠性。
在Web开发中,表单重复提交是一个常见的问题,它可能导致数据的不一致性或者服务端处理逻辑的混乱。Struts2框架提供了一种解决方案,即使用Token机制来防止表单的重复提交。以下是对这个主题的详细说明: 1. **表单...
在Web开发中,表单重复提交是一个常见的问题,可能导致数据不一致或服务器资源浪费。本文将深入探讨如何通过自定义标签来防止表单的重复提交,以确保数据的一致性和系统的稳定性。 首先,理解表单重复提交的问题。...
二、防止表单重复提交的方法 1. **使用JavaScript禁用提交按钮** 在表单提交时,可以使用JavaScript来禁用提交按钮,以防止用户连续点击。例如: ```javascript function disableSubmit() { document....
下面将详细解释Struts2如何通过重定向来防止表单重复提交。 首先,理解表单重复提交的场景:用户在提交表单后,由于网络延迟或其他原因,可能会无意中多次点击提交按钮。如果服务器没有处理这些重复请求,那么相同...
防止表单重复提交的方法通常有两种:客户端解决方案和服务器端解决方案。 1. 客户端解决方案: - JavaScript禁用提交按钮:在提交表单时,可以通过JavaScript立即禁用提交按钮,避免用户连续点击。 - 使用Ajax...
综上所述,理解并掌握Struts框架中的数据回显、模型驱动以及防止表单重复提交的原理和实现方法,对于提升Java Web应用的开发效率和质量具有重要意义。在实际开发中,应灵活运用这些技术,以满足项目需求,打造稳定...
总结来说,这个SSH笔记涵盖了Web开发中的关键知识点,包括数据验证确保输入合法性,文件上传下载提供交互性,防止表单重复提交保护数据一致性,以及自定义拦截器增强应用的功能和灵活性。掌握这些技能,将有助于提升...
使用Redis和Spring Boot来防止表单重复提交的基本思路是:在接收到表单提交请求后,生成一个唯一的请求标识(例如,基于UUID),并将其作为键存储到Redis中,设置一个适当的过期时间。当服务器接收到新的请求时,会...
在Java Web开发中,防止重复提交是一个重要的议题,尤其是在大型分布式应用中,因为这可能导致服务器资源的过度消耗,甚至引发系统性能问题。以下是一些防止重复提交的常见策略和技术: 1. **令牌(Token)机制**:...
Java表单重复提交是Web开发中常见的问题,可能导致数据不一致和系统异常。要理解这个问题,首先要明白表单提交的基本流程:用户打开表单页面,填写信息,然后点击提交按钮,浏览器向服务器发送请求,服务器处理请求...
在Web开发中,防止表单重复提交是一个常见的需求,尤其是在处理敏感数据或者执行不可逆操作时。Struts2为解决这个问题引入了Token机制,确保每个表单提交只被处理一次,避免了由于用户意外刷新页面导致的重复请求。 ...
防止页面刷新重复提交是 Web 开发中常见的问题,多次提交表单可能会导致不必要的数据重复录入、服务器压力增大等问题。下面我们将详细介绍防止页面刷新重复提交的方法。 一、验证码方法 验证码方法是防止页面刷新...
Struts2作为一个流行的Java Web框架,为解决表单重复提交提供了多种方法。 首先,关于表单重复提交的原因,有以下几点: 1. 服务器或网络延迟导致用户多次点击提交按钮。 2. 用户在表单提交后刷新浏览器页面。 ...
在Struts2中防止表单重复提交的过程主要包括以下几个步骤: 1. **生成Token**:当用户发起表单请求时,服务器会生成一个唯一的Token并将其存储在服务器的会话(Session)中,同时将这个Token作为隐藏字段放入到HTML...
表单重复提交是指在一次请求完成之前防止重复提交,解决表单重复提交有多种形式,以下以 Aop+自定义注解+Redis 为例来介绍。 解决方案的详细流程 1. 当页面加载时,前端请求后台,后台生成 token 缓存到 Redis ...
Struts是Java Web开发中的一款流行MVC框架,它简化了构建基于JSP的Web应用程序的过程。然而,Web应用在处理表单提交...通过对这些代码的学习和研究,开发者能够熟练地在自己的项目中应用Struts+Token防止表单重复提交。
综上所述,Struts框架通过Token机制有效地解决了表单重复提交的问题,提供了更健壮的Web应用安全性。正确理解和应用这一机制对于Java Web开发者来说至关重要。在实际项目中,结合其他防御策略,如CSRF防护,可以...