一:使用JavaScript來防止表單重復提交
有三種場景:1:在網絡延遲的情況下讓用戶有時間點擊多次submit導致重復提交
2:表單提交后點擊“刷新”按鈕導致重復提交
3:提交后,點擊瀏覽器的后退然后再次提交
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>Form表單</title>
<script type="text/javascript">
var isCommitted = false;//表單是否已經提交標識,默認為false
function dosubmit(){
if(isCommitted==false){
isCommitted = true;//提交表單后,將表單是否已經提交標識設置為true
return true;//返回true讓表單正常提交
}else{
return false;//返回false那么表單將不提交
}
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/DoFormServlet" onsubmit="return dosubmit()" method="post">
用戶名:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
另一種方式:將提交按鈕設置為不可用。
function dosubmit(){
//獲取表單提交按鈕
var btnSubmit = document.getElementById("submit");
//將表單提交按鈕設置為不可用,這樣就可以避免用戶再次點擊提交按鈕
btnSubmit.disabled= "disabled";
//返回true讓表單可以正常提交
return true;
}
js只能解決問題1,但是2,3解決不了。
利用session解決2和3:在服務器端解決
做法:在服務器端生成一個唯一的隨機標識號,專業術語:Token(令牌)。同時在當前用戶的Session中保存這個Token。然后將Token發送到客戶端的Form表單中
在表單中使用隱藏域來存儲這個Token。表單提交的時候連同這個Token一起提交到服務器端。然后在服務器端判斷客戶端提交上來的Token與服務器生成的是否一樣。不一樣就是重復提交了,此時服務器就不可以處理重復提交的表單。處理完后清除當前用戶的Session中存儲的標識號。
在下列情況中,服務器程序將拒絕處理
1:存儲session中的Token與表單提交的Token不同。
2:當前用戶的session中不存在token
3:用戶提交的表單數據中沒有Token
form.jsp頁面
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
<head>
<title>Form表單</title>
</head>
<body>
<form action="${pageContext.request.contextPath }/demo02" method="post">
<%--使用EL表達式取出存儲在session中的token--%>
<input type="hidden" name="token" value="${token}"/>
用戶名:<input type="text" name="username">
<input type="submit" value="提交">
</form>
</body>
</html>
業務處理
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
boolean b = isRepeatSubmit(request);//判斷用戶是否是重復提交
if(b==true){
System.out.println("請不要重復提交");
return;
}
request.getSession().removeAttribute("token");//移除session中的token
System.out.println("處理用戶提交請求!!");
}
/**
* 判斷客戶端提交上來的令牌和服務器端生成的令牌是否一致
* @param request
* @return
* true 用戶重復提交了表單
* false 用戶沒有重復提交表單
*/
private boolean isRepeatSubmit(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(!client_token.equals(server_token)){
return true;
}
return false;
}
服務器端保存:
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String token=TokenProccessor.getInstance().makeToken();
System.out.println("在FormServlet中生成的token:"+token);
request.getSession().setAttribute("token", token);//在服務器端保存
request.getRequestDispatcher("/form.jsp").forward(request, response);
}
生成token的工具類:
/*
* 生成Token的工具類
*/
public class TokenProccessor {
/*
* 單例設計模式(保證類的對象在內存中只有一個)
* 1:把類的構造函數私有
* 2:自己創建一個類的對象
* 3:對外提供一個公共的方法,返回類的對象
*/
private TokenProccessor(){}
private static final TokenProccessor instance = new TokenProccessor();
public static TokenProccessor getInstance(){
return instance;
}
public String makeToken(){ //checkException
// 7346734837483 834u938493493849384 43434384
String token = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";
//數據指紋 128位長 16個字節 md5
try {
MessageDigest md = MessageDigest.getInstance("md5");
byte md5[] = md.digest(token.getBytes());
//base64編碼--任意二進制編碼明文字符 adfsdfsdfsf
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(md5);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
