一、前端防護(hù)策略(減少無(wú)效請(qǐng)求)
1.按鈕禁用與狀態(tài)反饋
·點(diǎn)擊后立即禁用按鈕,阻止二次點(diǎn)擊,并添加加載動(dòng)畫(huà)提示用戶:document.getElementById("btnSubmit").addEventListener("click", function() {
this.disabled = true;
this.classList.add("loading-spinner"); // 添加加載樣式
});
2.防抖(Debounce)與請(qǐng)求鎖
·通過(guò)標(biāo)志位或定時(shí)器限制短時(shí)間內(nèi)的重復(fù)提交:
let isSubmitting = false;functionsubmitForm() {
if (isSubmitting) return;
isSubmitting = true;// 執(zhí)行提交邏輯
}
·框架中可使用lodash.debounce優(yōu)化
3.異步提交 + Loading提示
·使用Ajax提交數(shù)據(jù),配合模態(tài)框或進(jìn)度條增強(qiáng)用戶體驗(yàn):
printf("hello world!");$("#form").submit(function(e) {
e.preventDefault();
$("#loadingModal").show(); // 顯示加載提示
$.post("/submit", $(this).serialize(), function() {
$("#loadingModal").hide();
});
});
二、后端冪等性控制(核心防御)
1.Token令牌機(jī)制
·流程:生成頁(yè)面時(shí)創(chuàng)建唯一Token(如GUID)存入Session,嵌入表單隱藏域;提交時(shí)校驗(yàn)Token有效性并立即銷(xiāo)毀。
// ASP.NET MVC 示例public ActionResult SubmitForm(){
string token = Guid.NewGuid().ToString();
Session["SubmitToken"] = token;
ViewBag.Token = token; // 傳遞到視圖
}
[HttpPost]
public ActionResult SubmitForm(FormModel model, string token){
if (Session["SubmitToken"]?.ToString() != token) return Content("重復(fù)提交拒絕");
Session.Remove("SubmitToken");// 處理業(yè)務(wù)
}
·優(yōu)勢(shì):有效防御刷新、后退導(dǎo)致的重復(fù)提交
2.冪等鍵(Idempotency Key)
·客戶端生成唯一Key(如GUID)放入請(qǐng)求頭,服務(wù)端通過(guò)緩存(Redis/MemoryCache)校驗(yàn):
// ASP.NET Core 過(guò)濾器示例
publicclassIdempotencyFilter : IAsyncActionFilter{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next){
var key = context.HttpContext.Request.Headers["Idempotency-Key"].FirstOrDefault(); if (_cache.TryGetValue(key, out _)) context.Result = new BadRequestResult();
else _cache.Set(key, true, TimeSpan.FromMinutes(5));
await next();
}
}
3.重定向模式(PRG: Post-Redirect-Get)
·提交成功后返回302重定向至結(jié)果頁(yè),刷新時(shí)僅重發(fā)GET請(qǐng)求,避免重復(fù)POST。
三、數(shù)據(jù)庫(kù)與架構(gòu)層防護(hù)
1.數(shù)據(jù)庫(kù)唯一約束
·為業(yè)務(wù)關(guān)鍵字段(如訂單號(hào)、用戶郵箱)添加唯一索引,從底層阻止重復(fù)數(shù)據(jù):
ALTERTABLE Orders ADDUNIQUE (OrderNumber);
2.分布式鎖(Redis)
·以“用戶ID + 操作類(lèi)型”為Key加鎖,確保并發(fā)請(qǐng)求僅一個(gè)生效:
// Redis鎖示例
if (redisLock.AcquireLock(userId + "_submit", TimeSpan.FromSeconds(10))){
redisLock.ReleaseLock();
}
3.操作狀態(tài)校驗(yàn)
·更新數(shù)據(jù)前檢查狀態(tài)(如訂單是否已處理),避免重復(fù)更新:
UPDATE Orders SET Status ='Paid'WHERE Id =100AND Status ='Pending';
總結(jié)
·基礎(chǔ)方案:前端按鈕禁用 + 后端Token校驗(yàn)(覆蓋90%場(chǎng)景)
·高可靠場(chǎng)景:補(bǔ)充數(shù)據(jù)庫(kù)唯一索引與Redis分布式鎖
·用戶體驗(yàn)優(yōu)化:加載動(dòng)畫(huà)與防抖減少用戶焦慮性點(diǎn)擊