平时,如果网速较慢,用户提交表单后,如果服务器一段时间没有反应,用户很可能会再次点击提交,导致重复提交表单,因此开发中我们必须考虑到这一问题
- 方法一:在javascript中防止表单重复提交
<script type="text/javascript">
var isCommited = false;
function dosubmit() {
if (isCommited == false) {
isCommited = true;
return true;
}
else return false;
}
</script>
但是这种方式只能防止“网络延迟”这种情况,其他情况,e.g.用户后退到表单页面重新点击提交 or 提交后点击刷新再次提交 无法被解决
方法二:用session防止表单重复提交
具体做法:
1. 在服务器生成一个唯一的随机标识号(token),同时在当前用户的session中保存这个token
2. 将token发送到客户端的form表单中
3. 在form中隐藏存储这个token
4. 表单提交时将这个token**发回给服务器**
5. 服务器判断发过来的token与服务器端生成的token是否一致,如果不一致就是重复提交,服务器将不予处理;如果一致,则服务器处理,处理完后清除当前用户的session中存储的标识号(token)在以下集中情况,服务器对客户端的表单请求不予处理:
1. 存储session中的token与表单提交的token不一致
2. 当前用户session中不存在token
3. 用户提交的表单数据中没有token
FormServlet:用于生成token和跳转到form.jsp
@WebServlet(name = "FormServlet", value="/FormServlet")
public class FormServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String token = TokenProccessor.getInstance().makeToken();//创建令牌
System.out.println("生成token: " + token);
request.getSession().setAttribute("token", token);//在服务器使用session保存token
request.getRequestDispatcher("/form.jsp").forward(request, response);
}
}
form.jsp:使用隐藏域存储token
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<title>Form表单</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/DoFormServlet" method="post">
<%-- 隐藏存储的token --%>
<%--
<input type="submit" name="token" value="<%=session.getAttribute("token")%>">
--%>
<%-- 使用EL表达式取出存储在session的token --%>
<input type="hidden" name="token" value="${token}"/>
用户名:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
DoFormServlet:处理表单提交
@WebServlet(name = "DoFormServlet", value = "/DoFormServlet")
public class DoFormServlet extends HttpServlet {
/**
* 判断客户端提交的token与服务器端的是否一致
* @param request
* @return
* true: 用户重复提交了表单
* false: 用户没有重复提交表单
*/
private boolean isRepeatSummit(HttpServletRequest request){
String client_token = request.getParameter("token");
//1. 如果用户提交的表单数据中没有token,则是重复提交了表单
if (client_token == null) return true;
//取出存在session中的token
String server_token = (String) request.getSession().getAttribute("token");
//2. 如果用户的session中不存在token,则是重复提交了表单
if (server_token == null) return true;
//3. 如果用户存在session中的token与表单提交的token不同,则是重复提交了表单
if (!server_token.equals(client_token)) return true;
return false;
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
boolean b = isRepeatSummit(request);
//判断用户是否提交
if (b){
System.out.println("请不要重复提交");
return;
}
//移除session中的token
request.getSession().removeAttribute("token");
System.out.println("已处理用户提交请求");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
TokenProcessor:生成tokend的工具类
public class TokenProccessor {
/**
* 使用静态内部类方法实现单例模式
*/
private TokenProccessor(){
}
private static class Holder{
private static TokenProccessor instance = new TokenProccessor();
}
public static TokenProccessor getInstance(){
return Holder.instance;
}
public String makeToken(){
String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
//数据指纹
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("md5");
byte[] md5 = messageDigest.digest(token.getBytes());
//base64编码
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException();
}
}
}