通過JavaScript屏蔽(不推薦)
???????????????????????????????????????寫標(biāo)記falg? (return false擅这;)
兩種方式
??????????????????????????????????????禁用按鈕disabled(添加disable的屬性)
弊端:js代碼很容易被繞過厢洞。比如通過刷新頁面方式,或使用postman等工具繞過前段頁面仍能重復(fù)提交表單绩郎。因此不推薦此方法蔼水。
給數(shù)據(jù)庫增加唯一鍵約束(簡單粗暴)
在數(shù)據(jù)庫建表的時(shí)候在ID字段添加主鍵約束赖瞒。
用戶名女揭、郵箱、電話等字段加唯一性約束栏饮。確保數(shù)據(jù)庫只可以添加一條數(shù)據(jù)吧兔。
數(shù)據(jù)庫加唯一性約束sql:
alter table tableName_xxx add unique key uniq_xxx(field1, field2)
服務(wù)器及時(shí)捕捉插入數(shù)據(jù)異常:
??????? try {
???????????????xxxMapper.insert(user);
??????????? } catch(DuplicateKeyException e) {
???????????????logger.error("user already exist");
??????????? }
弊端:無法阻止惡意用戶重復(fù)提交表單(攻擊網(wǎng)站),服務(wù)器大量執(zhí)行sql插入語句袍嬉,增加服務(wù)器和數(shù)據(jù)庫負(fù)荷境蔼。
添加函數(shù)防抖
詳見函數(shù)去抖、節(jié)流
利用Session防止表單重復(fù)提交(推薦)
服務(wù)器返回表單頁面時(shí)伺通,會先生成一個(gè)subToken保存于session箍土,并把該subToen傳給表單頁面。
當(dāng)表單提交時(shí)會帶上subToken罐监,服務(wù)器攔截器Interceptor會攔截該請求吴藻,攔截器判斷session保存的subToken和表單提交subToken是否一致。若不一致或session的subToken為空或表單未攜帶subToken則不通過笑诅。
首次提交表單時(shí)session的subToken與表單攜帶的subToken一致走正常流程调缨,
然后攔截器內(nèi)會刪除session保存的subToken。
當(dāng)再次提交表單時(shí)由于session的subToken為空則不通過吆你。從而實(shí)現(xiàn)了防止表單重復(fù)提交。
使用:
mvc配置文件加入攔截器配置
<mvc:interceptors><mvc:interceptor><mvc:mapping path="/**"/><bean class="xxx.xxx.interceptor.AvoidDuplicateSubmissionInterceptor"/></mvc:interceptor></mvc:interceptors>
攔截器
package xxx.xxxx.interceptor;
importxxx.xxx.SubToken;
importorg.apache.struts.util.TokenProcessor;
importorg.springframework.web.method.HandlerMethod;
importorg.springframework.web.servlet.handler.HandlerInterceptorAdapter;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
public class AvoidDuplicateSubmissionInterceptor extends
HandlerInterceptorAdapter {
public AvoidDuplicateSubmissionInterceptor() {
??? }
@Override
public boolean preHandle(HttpServletRequestrequest,
????????????????????????????HttpServletResponse response, Object handler)
throwsException {
if (handler instanceofHandlerMethod) {
??????????? HandlerMethod handlerMethod= (HandlerMethod) handler;
??????????? Method method =handlerMethod.getMethod();
???????????SubToken annotation = method
???????????????????.getAnnotation(SubToken.class);
if (annotation != null){
booleanneedSaveSession = annotation.saveToken();
if(needSaveSession) {
?request.getSession(false)
???????????????????????????.setAttribute
(
"subToken",
???????????????????????????????????TokenProcessor.getInstance().generateToken(
???????????????????????????????????????????request));
??????????????? }
booleanneedRemoveSession = annotation.removeToken();
if(needRemoveSession) {
if(isRepeatSubmit(request)) {
return false;
??????????????????? }
??????????????????? request.getSession(
false).removeAttribute("subToken");
??????????????? }
??????????? }
??????? }
return true;
??? }
private boolean isRepeatSubmit(HttpServletRequest request){
??????? String serverToken = (String)
request.getSession(false).getAttribute(
"subToken");
if (serverToken == null){
return true;
??????? }
??????? String clinetToken =request.getParameter(
"subToken");
if (clinetToken == null){
return true;
??????? }
if(!serverToken.equals(clinetToken)) {
return true;
??????? }
return false;
??? }
}?
控制層 controller
@RequestMapping("/form")
//開啟一個(gè)Token@SubToken(saveToken
= true)
publicString form() {
return "/test/form";
}
@RequestMapping(value = "/postForm", method = RequestMethod.POST)
@ResponseBody//開啟Token驗(yàn)證俊犯,并且成功之后移除當(dāng)前Token@SubToken(removeToken = true)
public String postForm(String userName) {
?System.out.println(System.currentTimeMillis());
try{
???System.out.println(userName);
??? Thread.sleep(1500);
//暫停1.5秒后程序繼續(xù)執(zhí)行
? }
catch(InterruptedException e) {
??? e.printStackTrace();
?}
?System.out.println(System.currentTimeMillis());
return "1";
}
表單頁面
<%@ page contentType="text/html;charset=UTF-8"language="java" %><html><head><title>Title</title></head><body><form method="post"action="/postForm"><input type="text" name="userName"><inputtype="hidden"
name="subToken" value="${subToken}"><inputtype="submit" value="提交"></form></body></html>