解決思路:
- 表單不能由HTML頁面輸出彪杉,須有程序生成輸出
- 程序輸出表單時需在表單里添加一個隨機(jī)數(shù)打給瀏覽器
- 用戶提交表單時把這個隨機(jī)數(shù)帶給服務(wù)器程序
- 用戶帶過來的隨機(jī)數(shù)和服務(wù)器隨機(jī)數(shù)進(jìn)行比較奕剃,若一致則接受昼窗,否則不予接受
具體的做法:
在服務(wù)器端生成一個唯一的隨機(jī)標(biāo)識號鬼佣,專業(yè)術(shù)語稱為Token(令牌),同時在當(dāng)前用戶的Session域中保存這個Token辨绊。然后將Token發(fā)送到客戶端的Form表單中敞咧,在Form表單中使用隱藏域來存儲這個Token,表單提交的時候連同這個Token一起提交到服務(wù)器端柬脸,然后在服務(wù)器端判斷客戶端提交上來的Token與服務(wù)器端生成的Token是否一致他去,如果不一致,那就是重復(fù)提交了倒堕,此時服務(wù)器端就可以不處理重復(fù)提交的表單灾测。如果相同則處理表單提交,處理完后清除當(dāng)前用戶的Session域中存儲的標(biāo)識號垦巴。
在下列情況下媳搪,服務(wù)器程序?qū)⒕芙^處理用戶提交的表單請求:
- 存儲Session域中的Token(令牌)與表單提交的Token(令牌)不同。
- 當(dāng)前用戶的Session中不存在Token(令牌)骤宣。
- 用戶提交的表單數(shù)據(jù)中沒有Token(令牌)秦爆。
代碼實現(xiàn):
- form.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>form.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript">
var isCommitted = false;
function dosubmit()
{
if(isCommitted==false){
isCommitted = true;
return true;
}else{
return false;
}
}
function dosubmit(){
var submit = document.getElementById("submit");
submit.disabled= "disabled";
return true;
}
</script>
</head>
<body>
<form action="~這里是你表單提交的servlet路徑~/FormServlet" method="post" onsubmit="return dosubmit()">
用戶名:<input type="text" name="username">
<input type="submit" value="提交" id="submit">
</form>
</body>
</html>
- 負(fù)責(zé)產(chǎn)生表單的servlet
這里隨機(jī)數(shù)產(chǎn)生器類TokenProccessor{}請看全局隨機(jī)數(shù)生成器的設(shè)計
//負(fù)責(zé)產(chǎn)生表單
public class FormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
String token = TokenProccessor.getInstance().makeToken();
request.getSession().setAttribute("token", token); //在服務(wù)器端保存隨機(jī)數(shù)
out.println("<form action='/day07/servlet/DoFormServlet' method='post'>");
out.write("<input type='hidden' name='token' value='"+token+"'>");
out.println("用戶名:<input type='text' name='username'>");
out.println("<input type='submit' value='提交'>");
out.println("</form>");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
- 表單提交處理,如果不是不是重復(fù)提交則進(jìn)行處理
public class DoFormServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
boolean b = isToken(request); //判斷用戶是否是重復(fù)提交
if(b==true){
System.out.println("請不要重復(fù)提交");
return;
}
request.getSession().removeAttribute("token");//移除session中的token
System.out.println("處理用戶提交請求c九等限!");
}
/**
* 判斷客戶端提交上來的令牌和服務(wù)器端生成的令牌是否一致
* @param request
* @return
* true 用戶重復(fù)提交了表單
* false 用戶沒有重復(fù)提交表單
*/
private boolean isToken(HttpServletRequest request) {
String client_token = request.getParameter("token");
//1爸吮、如果用戶提交的表單數(shù)據(jù)中沒有token,則用戶是重復(fù)提交了表單
if(client_token==null){
return true;
}
//取出存儲在Session中的token
String server_token = (String) request.getSession().getAttribute("token");
//2望门、如果當(dāng)前用戶的Session中不存在Token(令牌)形娇,則用戶是重復(fù)提交了表單
if(server_token==null){
return true;
}
//3、存儲在Session中的Token(令牌)與表單提交的Token(令牌)不同筹误,則用戶是重復(fù)提交了表單
if(!client_token.equals(server_token)){
return true;
}
return false;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}