一、重复提交的状况html
①、在表单提交到一个Servlet,而Servlet又经过请求转发的方式响应了一个JSP(HTML)页面,此地址栏还保留着Servlet的那个路径,而响应页面点击“刷新”。java
②、在响应页面没有达到时重复点击“提交按钮”。git
③、点击返回,再点击提交。(返回刷新在提交就不算重复提交)spring
二、如何避免表单重复提交:在表单中作一个标记,提交到servlet时检查标记是否存在且是否和预约义的标记一致,若一致则受理请求并销毁标记;若不一致或没有标记,则直接响应提示信息”重复提交“。session
步骤:app
>在源表单页面,生成一个随机token框架
>在原表单页面,把token值放入session属性中jsp
>在源表单页面把token值放入到隐藏域中。post
提交表单:this
>在目标的Servlet中:获取Session和隐藏域中的token。
>比较两个值是否一致,若一致受理请求,并把session域中的token清除,若不一致,则直接响应提示页面:“重复提交“
以上也是struts、springMvc等框架防止表单重复提交的原理。
实例代码:
<!--index.jsp --> <body> <% String tokenValue = new Date().getTime()+""; session.setAttribute("token", tokenValue); %> <form action="<%=request.getContextPath()%>/tokenServlet" method="post"> <input type="hidden" name="token" value="<%=tokenValue%>"> name:<input type="text" name="name" /> <input type="submit" value="提交"> </form> </body> <!--token.jsp--> <body> 重复提交 </body> 响应servlet @WebServlet(name = "tokenServlet", urlPatterns = "/tokenServlet") public class TokenServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } Object token = session.getAttribute("token"); String tokenValue = request.getParameter("token"); System.out.println(token); System.out.println(tokenValue); if(token != null && token.equals(tokenValue)){ session.removeAttribute("token"); }else{ response.sendRedirect(request.getContextPath()+"/token/token.jsp"); return; } String name = request.getParameter("name"); System.out.println("name:" + name); //request.getRequestDispatcher("/token/success.jsp").forward(request, response); response.sendRedirect(request.getContextPath()+"/token/success.jsp"); } }
上面的代码不够通用,查看struts防止表单提交源码,原理相同,以下:
//TokenProcesser.java package com.mengqi.servlet; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public class TokenProcessor { private static final String TOKEN_KEY = "COM.ATGUIGU.TOKEN_KEY"; private static final String TRANSACTION_TOKEN_KEY = "TRANSACTION_TOKEN_KEY"; private static TokenProcessor instance = new TokenProcessor(); private long previous; protected TokenProcessor() { super(); } public static TokenProcessor getInstance() { return instance; } public synchronized boolean isTokenValid(HttpServletRequest request) { return this.isTokenValid(request, false); } public synchronized boolean isTokenValid(HttpServletRequest request, boolean reset) { HttpSession session = request.getSession(false); if (session == null) { return false; } String saved = (String) session.getAttribute(TRANSACTION_TOKEN_KEY); if (saved == null) { return false; } if (reset) { this.resetToken(request); } String token = request.getParameter(TOKEN_KEY); if (token == null) { return false; } return saved.equals(token); } public synchronized void resetToken(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session == null) { return; } session.removeAttribute(TRANSACTION_TOKEN_KEY); } public synchronized String saveToken(HttpServletRequest request) { HttpSession session = request.getSession(); String token = generateToken(request); if (token != null) { session.setAttribute(TRANSACTION_TOKEN_KEY, token); } return token; } public synchronized String generateToken(HttpServletRequest request) { HttpSession session = request.getSession(); return generateToken(session.getId()); } public synchronized String generateToken(String id) { try { 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.getBytes()); 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(); } } //TokenServlet @WebServlet(name = "tokenServlet", urlPatterns = "/tokenServlet") public class TokenServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); try { Thread.sleep(2000); } catch (Exception e) { e.printStackTrace(); } boolean valid = TokenProcessor.getInstance().isTokenValid(request); if(valid){ TokenProcessor.getInstance().resetToken(request); }else{ response.sendRedirect(request.getContextPath()+"/token/token.jsp"); return; } String name = request.getParameter("name"); System.out.println("name:" + name); //request.getRequestDispatcher("/token/success.jsp").forward(request, response); response.sendRedirect(request.getContextPath()+"/token/success.jsp"); } } index.jsp <body> <form action="<%=request.getContextPath()%>/tokenServlet" method="post"> <input type="hidden" name="TOKEN_KEY" value="<%=TokenProcessor.getInstance().saveToken(request)%>"> name:<input type="text" name="name" /> <input type="submit" value="提交"> </form> </body>