如何解決重複下單的問題?(下單防重,重放攻擊)
阿新 • • 發佈:2020-07-23
如何解決重複下單的問題?(下單防重,重放攻擊)
問題來源:
近日發現某些使用者對一些商品進行多次下單,而且比較頻繁,而且下單時間都在同一秒內,懷疑產生重複請求
問題描述:
使用者在商品頁面,多次點選下單按鈕,後臺怎麼知道是使用者對一個商品進行多次下單,還是人工誤操作或者客戶端異常進行重複請求下單了呢?
解決方案:
- 進入商品頁面,就是下單頁面的時候,產生一個不重複的隨機數。
- 下單的時候把這個隨機數帶上
- 下單校驗的時候,利用Redis快取鎖,先鎖這個隨機數,再做業務處理,做完再釋放。
問題升級:
如果客戶端重複的下單請求過多,或者後臺處理過快,就會產生所謂的重放攻擊。
如,客戶端請求模擬
請求A 20:18:01 xxx/order?storeid=1&randomNum=abcd123 使用者下單
請求B 20:18:01 xxx/order?storeid=1&randomNum=abcd123 使用者下單
請求B 獲得鎖失敗
請求C 20:18:01 xxx/order?storeid=1&randomNum=abcd123 使用者下單
請求C 獲得鎖失敗
請求A 下單成功,釋放鎖
請求D 20:18:01 xxx/order?storeid=1&randomNum=abcd123 使用者下單
請求D 獲得鎖成功,開始處理
請求D 下單成功,釋放鎖
針對同一個隨機數randomNum=abcd123居然下單成功2次,這就是重複攻擊帶來的危害
問題升級,解決方案:
- 下單校驗的時候,利用Redis快取鎖,先鎖這個隨機數,鎖成功之後,判斷隨機數是否曾經處理過,如果沒有就把隨機數加入快取設定時長為X,再做業務處理,做完再釋放
/** * 獲得鎖 * @param lockId * @return */ public boolean getLock(String lockId) { try { String KEY_LOCK_ID_="LOCK_ID_"; String KEY_LOCK_HIS_ID_="LOCK_HIS_ID_"; Boolean success = redisTemplate.opsForValue().setIfAbsent(KEY_LOCK_ID_+lockId, "lock"); //解決重放攻擊 if(success != null && success){ if(hasKey(KEY_LOCK_HIS_ID_+lockId)){ success = false; logger.error("【REDIS操作】【獲得鎖失敗】【已存在歷史鎖碼】【懷疑重放攻擊:"+lockId+"】"); }else{ success = true; set(KEY_LOCK_HIS_ID_+lockId,lockId,60*60*24); } } return success; } catch (Exception e) { logger.error("【REDIS操作】【獲得鎖錯誤】",e); return false; } } /** * 釋放鎖 * @param lockId */ public void releaseLock(String lockId) { try { String KEY_LOCK_ID_="LOCK_ID_"; redisTemplate.delete(KEY_LOCK_ID_ + lockId); } catch (Exception e) { logger.error("【REDIS操作】【釋放鎖錯誤】",e); } }