首先我认为,要想从根本上避免用户重复提交表单,就一定要采用token令牌。使用js脚本在一定程度上能避免此类事情发生,但我认为这样做有缺陷。首先,利用js代码避免用户二次提交表单,在某些特殊情况下并不能起作用,详情请参见:http://www.cnblogs.com/xdp-gacl/p/3859416.html。个人认为最好的方法是综合采用token令牌和js脚本。
1、TokenUtil这个类主要用于生成token令牌(感谢_cuiyaoqiang的原创文章:http://blog.csdn.net/cuiyaoqiang/article/details/50960787):
package com.java.token;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import sun.misc.BASE64Encoder;
public class TokenUtil {
/*
*单例设计模式(保证类的对象在内存中只有一个)
*1、把类的构造函数私有
*2、自己创建一个类的对象
*3、对外提供一个公共的方法,返回类的对象
*/
private TokenUtil(){
}
private static final TokenUtil instance = new TokenUtil();
/**
* 返回类的对象
* @return
*/
public static TokenUtil getInstance(){
return instance;
}
/**
* 生成Token
* Token:Nv6RRuGEVvmGjB+jimI/gw==
* @return
*/
public String makeToken(){
String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
//数据指纹 128位长 16个字节 md5
try {
MessageDigest md = MessageDigest.getInstance("md5");
//对于给定数量的更新数据,digest 方法只能被调用一次。digest 方法被调用后,MessageDigest对象被重新设置成其初始状态。
byte md5[] = md.digest(token.getBytes());
//base64编码--任意二进制编码明文字符 adfsdfsdfsf
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
2、类TokenTools主要用于在session中设置token令牌,和验证令牌:
package com.java.token;
import javax.servlet.http.HttpSession;
public class TokenTools {
public TokenTools(){}
/* *功能:生成token令牌,并保存在session中
* *参数:tokenname是在session中token的名称
* * session是要存储token的目标session
* *返回值:返回session字符串
* */
public static String setToken(String tokenname, HttpSession session)
{
String token = TokenUtil.getInstance().makeToken();
session.setAttribute(tokenname, token);
return token;
}
/* *功能:验证token是否存在
* *参数:tokenname是在session中token的名称
* * token是从表单中提取的令牌值
* * session是存储token的目标session
* *返回值:如果令牌正确返回true,反之为false
* */
public static boolean verifyToken(String tokenname, String token, HttpSession session)
{
if(session.getAttribute(tokenname)==null)
{
return false;
}
String tokenInsession = session.getAttribute(tokenname).toString();
//如果验证令牌通过,就重置session属性
if(tokenInsession.equals(token))
{
session.removeAttribute(tokenname);
return true;
}
return false;
}
}
3、表单页面jsp,注意第17行,那里生成一个token:
<%@page import="com.java.token.TokenTools"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<%
session.setMaxInactiveInterval(30*60);
/*设置token令牌*/
String token = TokenTools.setToken("regist_token", session);
//System.out.println("jsp: "+token);
%>
用户注册
用户注册
4、在servlet中验证token:
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
req.setCharacterEncoding("UTF-8");
String username,password,sex,note, token;
token = req.getParameter("regist_token");
//验证token1是否合法
HttpSession session = req.getSession();
//System.out.println("servlet: "+session.getAttribute("regist_token").toString());
boolean pass = TokenTools.verifyToken("regist_token", token, session);
if(pass==false)
{
System.out.println("token repeat submit!");
req.setAttribute("msg", "请不要重复提交表单");
req.getRequestDispatcher("/regist.jsp").forward(req, resp);
return;
}
username = req.getParameter("username");
password = req.getParameter("password");
sex = req.getParameter("sex");
note = req.getParameter("note");
if(username==null||password==null
||sex==null||note==null)
{
String path = req.getContextPath();
String basePath = req.getScheme()+"://"+
req.getServerName()+":"+
req.getServerPort()+path+"/";
resp.sendRedirect(basePath+"regist.jsp");
return;
}
session.setAttribute("username", username);
session.setAttribute("note", note);
session.setAttribute("sex", sex);
session.setAttribute("password", password);
req.getRequestDispatcher("/usermain.jsp").forward(req, resp);
return;
}