用戶重復提交表單在某些場合將會造成非常嚴重的后果。例如,在使用信用卡進行在線支付的時候,如果服務器的響應速度太慢,用戶有可能會多次點擊提交按鈕,而這可能導致那張信用卡上的金額被消費了多次。因此,重復提交表單會對你的系統帶來邏輯影響,必須采取一些措施防止這類情況的發生。
用戶重復提交同一個HTML表單的原因有: 一、快速多次點擊了提交按鈕;二、提交表單后按下瀏覽器的刷新按鈕。
設置Struts 2的預防表單重復提交的功能
Struts 2已經內置了能夠防止用戶重復提交同一個HTML表單的功能。它的工作原理:讓服務器生成一個唯一標記,并在服務器和表單里各保存一份這個標記的副本。此后,在用戶提交表單的時候,表單里的標記將隨著其他請求參數一起發送到服務器,服務器將對他收到的標記和它留存的標記進行比較。如果兩者匹配,這次提交的表單被認為是有效的,服務器將對之做出必要的處理并重新設置一個新標記。隨后,提交相同的表單就會失敗,因為服務器上的標記已經重置。
Struts 2標簽中的token標簽,可以用來生成一個獨一無二的標記。這個標記必須嵌套在form標簽中使用,它會在表單里插入一個隱藏字段并把標記保存到HttpSession對象里。toke標簽必須與Token或Token Session攔截器配合使用,兩個攔截器都能對token標簽進行處理。Token攔截器遇到重復提交表單的情況,會返回一個"invalid.token"結果并加上一個動作級別的錯誤。Token Session攔截器擴展了Token攔截器并提供了一種更復雜的服務,它采取的做法與Token攔截器不同,它只是阻斷了后續的提交,這樣用戶不提交多少次,就好像只是提交了一次。
示例:使用Token攔截器預防表單重復提交
1. 配置struts.xml文件,聲明動作
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="avoidPackage" extends="struts-default">
<action name="avoid" class="struts2.action.AvoidAction">
<interceptor-ref name="token"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="invalid.token">/error.jsp</result>
<result name="input">/input.jsp</result>
<result name="success">/output.jsp</result>
</action>
</package>
</struts>
此時,需要在動作的聲明中,為動作添加token攔截器,因為token攔截器不在defaultStack攔截器棧中,注意,需要將攔截器放在攔截器棧的第一位,這是因為判斷表單是否被重復提交的邏輯應該在表單處理前。
2. 創建動作類
public class AvoidAction extends ActionSupport {
private static final long serialVersionUID = 2676453800249807631L;
private String username;
private Date birthday;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String execute()
{
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return SUCCESS;
}
}
這個動作邏輯處理為掛起4秒鐘,讓我們有機會多次點擊提交按鈕,測試效果。
3. 創建頁面:
input.jsp
<s:form action="avoid">
<s:token></s:token>
<s:textfield name="username" label="Enter your name"></s:textfield>
<s:textfield name="birthday" label="Enter your birthday"></s:textfield>
<s:submit value="submit"></s:submit>
</s:form>
要使用Struts 2的防止表單重復提交功能,需要在form標簽中使用token標簽,他會產生一個唯一的標識符,與其他參數一起提交到服務器,服務器會根據token標簽所產生的標識符判斷表單是否為重復提交的表單,這個功能是由Token攔截器完成的。
error.jsp
<body>
do not duplicate submissions form!
</body>
當表單重復提交,Token攔截器會返回一個"invalid.token"結果,結果將頁面轉到這個頁面,提示用戶錯誤信息。
output.jsp
<body>
Your Name : <s:property value="username"/>
<br />
Your Birthday : <s:property value="birthday"/>
</body>
若沒有重復提交表單,那么就顯示正確的頁面。
4. 測試
在瀏覽器中輸入:http://localhost:8081/AvoidDuplicateSubmissions/input.jsp,得到如下界面

連續多次點擊"submit"按鈕,查看效果

可以看到,token攔截器的設置生效了,他阻止了表單的重復提交,并給出了錯誤提示
這次我們只點擊一次提交(請重新輸入URL,或后退到輸入頁面后刷新一下,這是因為token的標示在提交一次后已被修改,不刷新標示符是不可能與服務器存留的標示符一致的)

可以看到,表單被正確的處理了。
處理表單重復提交的另一個攔截器是 tokenSession,使用該攔截器與使用token攔截器并沒有什么差異只需要,引用該攔截器,其他與token攔截器完全一致
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="avoidPackage" extends="struts-default">
<action name="avoid" class="struts2.action.AvoidAction">
<interceptor-ref name="tokenSession"></interceptor-ref>
<interceptor-ref name="defaultStack"></interceptor-ref>
<result name="invalid.token">/error.jsp</result>
<result name="input">/input.jsp</result>
<result name="success">/output.jsp</result>
</action>
</package>
</struts>