一、重複提交的狀況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>