表單重復提交指未完成完整「請求頁面 → 提交表單」流程就多次提交。根本原因:缺乏請求唯一性標識。解決方案:令牌機制——頁面生成 Token,Session 存一份,表單攜帶提交,服務端驗證銷毀。
@WebServlet("/trans")
public class TransferServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) {
// 模擬網絡延遲 3 秒
Thread.sleep(3000);
String money = req.getParameter("money");
System.out.println("轉出金額: " + money); // 瘋狂點擊會打印多次
}
}
<%@ page contentType="text/html;charset=UTF-8" %>
<h3>轉賬界面</h3>
<form action="/trans" method="post">
<input type="hidden" name="token" value="${token}"/>
轉賬金額: <input type="text" name="money" min="1" required /><br/>
<input type="submit" value="轉賬" />
</form>
@WebServlet("/transfer")
public class TransferPageServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String token = UUID.randomUUID().toString();
req.getSession().setAttribute("TOKEN_IN_SESSION", token);
req.setAttribute("token", token);
req.getRequestDispatcher("/views/submit.jsp").forward(req, resp);
}
}
@WebServlet("/trans")
public class TransferServlet extends HttpServlet {
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String token = req.getParameter("token");
String sessionToken = (String) req.getSession().getAttribute("TOKEN_IN_SESSION");
if (token != null && token.equals(sessionToken)) {
// 驗證通過:刪除 Token,防止重復提交
req.getSession().removeAttribute("TOKEN_IN_SESSION");
String money = req.getParameter("money");
System.out.println("轉出金額: " + money);
// 業務邏輯...
} else {
// 重復提交或 Token 失效
resp.getWriter().write("請勿重復提交");
}
}
}
public class TokenUtil {
private static final String TOKEN_KEY = "TOKEN_IN_SESSION";
// 生成並存儲 Token
public static void saveToken(HttpServletRequest req) {
String token = UUID.randomUUID().toString();
req.getSession().setAttribute(TOKEN_KEY, token);
req.setAttribute("token", token); // 供頁面使用
}
// 驗證並銷毀 Token
public static boolean validateToken(HttpServletRequest req, String requestToken) {
String sessionToken = (String) req.getSession().getAttribute(TOKEN_KEY);
if (requestToken != null && requestToken.equals(sessionToken)) {
req.getSession().removeAttribute(TOKEN_KEY);
return true;
}
return false;
}
}
@GetMapping("/transfer")
public String showPage(HttpServletRequest req) {
TokenUtil.saveToken(req);
return "transfer";
}
@PostMapping("/trans")
public String transfer(HttpServletRequest req, @RequestParam String money) {
String token = req.getParameter("token");
if (!TokenUtil.validateToken(req, token)) {
return "error/repeat-submit";
}
// 業務處理...
return "success";
}
Token 令牌機制是防重復提交經典方案:生成→存儲→攜帶→驗證→銷毀 閉環。工具類封裝消除重複代碼,分佈式場景遷移至 Redis,配合前端防抖、數據庫約束構建多層防護體系。