表单重复提交的情况大体有两种:一种是网速卡,造成表单没有提交的假象,用户不停的点击提交。另一种则是用户的故意操作,后退或刷新页面然后重新提交。要做的就是防止这两种情况的表单重复提交。
防止第一种,我们可以在客户端添加操作防止现象发生,有两种方式,都是通过javascript实现的。第一种方式是在javascript中设置一个变量。初值为false,点击提交按钮之后将变量改为true,并且,在变量为false时按钮有提交作用。代码如下:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>ReapteForm.html</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var competeSign = false;
function check() {
if(!competeSign) {
competeSign = true;
return competeSign;
}else {
alert("不能重复提交表单");
return false;
}
}
</script>
</head>
<body style="background-color: white;">
<form action="/learnJS/servlet/RepeateFormServlet" method="post" οnsubmit="return check()">
留言内容:<br/>
<textarea rows="10" cols="50"></textarea><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
javascript的第二种方式是,在按钮点击之后变为不可用,这样就不能再提交啦。代码为:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript">
function dosubmit() {
var input = document.getElementById("submit");
input.disabled = 'disabled';
return true;
}
</script>
</head>
<body style="background-color: white;">
<form action="/learnJS/servlet/RepeateFormServlet" method="post" οnsubmit="return dosubmit();">
留言内容:<br/>
<textarea rows="10" cols="50"></textarea><br/>
<input type="submit" id="submit" value="提交"/>
</form>
</body>
</html>
再有就是在服务器端通过session方式防止表单的重复提交,这样只能防止后退操作,不能防止刷新操作的。思路是,在页面的表单中有一个隐藏项,每一个表单都有一个唯一的标号,同时session中设置一个属性域,通过标号和属性域的比对操作,确定表单是否为重复提交。页面代码为:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@page import="com.you.learn.TokenProcessor"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>FormGenerate.jsp</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript">
function beformsubmit() {
var p1 = document.getElementById("p1").value;
if(p1==""){
alert("请输入内容!");
return false;
}
return true;
}
</script>
</head>
<body style="background-color: white;">
<%
TokenProcessor.getInstance().saveToken(request);
String token = (String)request.getSession().getAttribute(TokenProcessor.FORM_TOKEN_KEY);
String name = TokenProcessor.FORM_TOKEN_KEY;
System.out.println("html:" + token);
%>
<form action="/learnJS/servlet/FormDealServlet" method="post" οnsubmit="return beformsubmit();">
<input type="hidden" name="<%=name %>" value="<%=token %>">
字段1:<input type="text" name="p1" id="p1"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
处理页面的servlet页面代码为:
package com.you.servlet;
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;
import com.you.learn.TokenProcessor;
public class FormDealServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
request.getParameter("TokenProcessor.FORM_TOKEN_KEY");
TokenProcessor tokenProcessor = TokenProcessor.getInstance();
if(!tokenProcessor.isTokenValid(request)) {
out.println("这是重复或非法提交!");
response.sendRedirect("/learnJS/FormGenerate.jsp");
return;
}
String p1 = request.getParameter("p1");
if(p1 == null || p1.trim().equals("")) {
out.println("请输入内容!");
}else {
out.println("提交内容已被处理!");
tokenProcessor.resetToken(request);
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
还有一个Java工具类,用于生成标号:
package com.you.learn;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import sun.misc.BASE64Encoder;
public class TokenProcessor {
private long previous;//上次生成表单标识号的时间值
private static TokenProcessor instance = new TokenProcessor();
public static String FORM_TOKEN_KEY = "FORM_TOKEN_KEY";
private TokenProcessor() {
}
@SuppressWarnings("unused")
public static TokenProcessor getInstance() {
return instance;
}
/*
* 验证请求消息中的标识号是否有效,如果验证请求消息中的标识号和
* 用户session域中的标识号相同,返回结果为true,否则返回false
*
*/
public synchronized boolean isTokenValid(HttpServletRequest request) {
/*
* 为避免session对象不存在时创建session对象,
* 下面的语句不用request.getSession()
*/
HttpSession session = request.getSession(false);
if(session == null) {
return false;
}
String saved = (String)request.getSession().getAttribute("FORM_TOKEN_KEY");
System.out.println("Token:" + saved);
if(saved == null) {
return false;
}
String token = request.getParameter("FORM_TOKEN_KEY");
System.out.println("Token+token:" + token);
if(token == null) {
return false;
}
System.out.println("equals:" + saved.equals(token));
return saved.equals(token);
}
/**
* 清除存储在当前用户session中的表单标识号
*/
public synchronized void resetToken(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if(session == null) {
return;
}
session.removeAttribute(FORM_TOKEN_KEY);
}
/**
* 产生表单标识号,并将保存到当前用户的session中
*/
public synchronized void saveToken(HttpServletRequest request) {
HttpSession session = request.getSession();
try {
byte[] id = session.getId().getBytes();
long current = System.currentTimeMillis();
if(current == previous) {
current++;
}
previous = current;
byte[] now = String.valueOf(current).getBytes();
//获得随机数摘要(128个字节)
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(id);
md.update(now);
//base64编码
BASE64Encoder encoder = new BASE64Encoder();
String token = encoder.encode(md.digest());
session.setAttribute(FORM_TOKEN_KEY, token);
System.out.println("java:" + session.getAttribute("FORM_TOKEN_KEY"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
}
这样就完成了。