1. 程式人生 > 實用技巧 >如何解決重複下單的問題?(下單防重,重放攻擊)

如何解決重複下單的問題?(下單防重,重放攻擊)

如何解決重複下單的問題?(下單防重,重放攻擊)

問題來源:

近日發現某些使用者對一些商品進行多次下單,而且比較頻繁,而且下單時間都在同一秒內,懷疑產生重複請求

問題描述:

使用者在商品頁面,多次點選下單按鈕,後臺怎麼知道是使用者對一個商品進行多次下單,還是人工誤操作或者客戶端異常進行重複請求下單了呢?

解決方案:

  1. 進入商品頁面,就是下單頁面的時候,產生一個不重複的隨機數。
  2. 下單的時候把這個隨機數帶上
  3. 下單校驗的時候,利用Redis快取鎖,先鎖這個隨機數,再做業務處理,做完再釋放。

問題升級:

如果客戶端重複的下單請求過多,或者後臺處理過快,就會產生所謂的重放攻擊。
如,客戶端請求模擬
請求A 20:18:01 xxx/order?storeid=1&randomNum=abcd123 使用者下單

請求A 獲得鎖成功,開始處理
請求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次,這就是重複攻擊帶來的危害

問題升級,解決方案:

  1. 下單校驗的時候,利用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); 
        }
    }