重复提交表单原因:
1.前台多次按了“提交”接钮;
通过在前台按钮的onclick事件来避免
<script language="javascript"> <!-- var checkSubmitFlg=true; function checkSubmit() { if(true==checkSubmitFlg) { document.theForm.submit(); checkSubmitFlg=false; } else { alert("你已经提交了表单,请不要重复提交!"); } } //--> </script>
2.在浏览器按“刷新”按钮
在相关的页面的表单上增加TokenProcessor隐藏域(input type="hidden")。
(1)在后台建立TokenProcessor类
package com.cjg.servlet;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* TokenProcessor类是一个单例类。
*/
public class TokenProcessor
{
static final String TOKEN_KEY="org.sunxin.token";
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中,
* 如果当前Session不存在,则创建一个新的Session。
*/
public synchronized void saveToken(HttpServletRequest request)
{
HttpSession session = request.getSession();
String token = generateToken(request);
if (token != null) {
session.setAttribute(TOKEN_KEY, token);
}
}
/**
* 根据用户会话ID和当前的系统时间生成一个唯一的令牌。
*/
public synchronized String generateToken(HttpServletRequest request)
{
HttpSession session = request.getSession();
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)
{
return null;
}
}
/**
* 将一个字节数组转换为一个十六进制数字的字符串。
*/
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;
}
}
(2)在表单加入TokenProcessor隐藏域
<body>
<%
TokenProcessor processor=TokenProcessor.getInstance();
String token=processor.getToken(request);
%>
<form method="post" action="handler" name="theForm">
<table>
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td>
<input type="password" name="password">
<input type="hidden" name="org.sunxin.token" value="<%=token%>"/>
</td>
</tr>
<tr>
<td><input type="reset" value="重填"></td>
<td><input type="button" name="btnSubmit" value="提交" onClick="checkSubmit();"/></td>
</tr>
</table>
</form>
</body>
(3)处理form的Servlet
package com.cjg.servlet;
import javax.servlet.*;
import java.io.*;
import javax.servlet.http.*;
import java.util.*;
import com.cjg.servlet.TokenProcessor;
public class HandlerServlet extends HttpServlet
{
int count=0;
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException,IOException
{
resp.setContentType("text/html;charset=GBK");
PrintWriter out=resp.getWriter();
TokenProcessor processor=TokenProcessor.getInstance();
if(processor.isTokenValid(req))
{
try
{
Thread.sleep(5000);
}
catch(InterruptedException e)
{
System.out.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();
}
}