簡易首頁防暴力-字典計時器

       有時候首頁需要限制下相同賬號的錯誤登錄次數,防止暴力破解,實際而言,還是有一點點作用,雖然並不是很大,一定層度上也能扼殺一番,主要是調整起來方便,對於老舊系統改造起來比較快,核心是字典,一個記錄失敗次數,一個記錄賬號解鎖的時間,在賬號登錄時先去字典裏面校驗,不用頻繁的請求數據庫.  需要注意的是,這個字典要設置爲全局。否則切換客服端就會失效.

        //次數字典
        private static Dictionary<string, int> errorCounts = new Dictionary<string, int>();
        //時間字典
        private static Dictionary<string, DateTime> lockoutTimes = new Dictionary<string, DateTime>();

字典設置完畢,接下來就是在登錄的時機點上校驗次數,基本思路是,每個登錄進來的賬號無論密碼,先加到字典中,設置一個初始時間,後邊後續統一判斷

        public ActionResult ICCLogin(string userName, string password)
        {
            int result;
            DateTime dateTime;
            //先加時間字典
            if (!errorCounts.TryGetValue(userName, out result))
            {
                errorCounts[userName] = 0;
                lockoutTimes[userName] = DateTime.MinValue;
            }
            //判斷次數字典
            if (IsLockedOut(userName, out dateTime))
            {
                // 計算兩個日期時間之間的時間間隔
                TimeSpan timeDifference = dateTime.Subtract(DateTime.Now);
                // 計算總分鐘數並向上取整
                int totalMinutes = (int)Math.Ceiling(timeDifference.TotalMinutes);
                // 如果向上取整後的分鐘數小於1,設爲30
                if (totalMinutes == 1)
                {
                    totalMinutes = 30;
                }
                string suf = totalMinutes == 30 ? "s" : "分鐘";
                LibExceptionManagent.ThrowErr(string.Format("驗證失敗次數過多,賬戶已被鎖定,{0}{1}後重試", totalMinutes, suf));
                return View();
            }

次數字典方法

        private bool IsLockedAccount(string username, out DateTime dateTime)
        {
            //先加次數字典
            if (!lockoutTimes.ContainsKey(username))
            {
                lockoutTimes[username] = DateTime.MinValue;
            }
            //獲取當前賬號的可放開時間
            dateTime = lockoutTimes[username];
            return lockoutTimes[username] > DateTime.Now;
        }

貿然看去,貌似沒啥子問題, 實際上還缺少一個歸零的操作,到達賬號解封時間後, 需要置空錯誤次數,否則就會無限循環,5分鐘結束後又從頭開始

        private bool IsLockedOut(string username, out DateTime dateTime)
        {
            //先加次數字典
            if (!lockoutTimes.ContainsKey(username))
            {
                lockoutTimes[username] = DateTime.MinValue;
            }

            dateTime = lockoutTimes[username];
            bool bol = lockoutTimes[username] > DateTime.Now;
            //獲取當前賬號的可放開時間
            if (bol && errorCounts.TryGetValue(username, out _))
            {
                errorCounts[username] = 0;
            }
            return bol;
        }

如此基本上滿足次數校驗,爲了形成一個小小的閉環,全局靜態字典需要回收,再寫一個定時任務清空字典,避免字典值越來越大

        private static readonly Timer timer = new Timer(ClearDictionary, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));

        private static void ClearDictionary(object state)
        {
            // 在定時器觸發時清空字典
            List<string> keysToRemove = lockoutTimes.Where(pair => pair.Value != DateTime.MinValue && pair.Value <= DateTime.Now).Select(pair => pair.Key).ToList();
            foreach (string key in keysToRemove)
            {
                lockoutTimes.Remove(key);
                if (errorCounts.TryGetValue(key, out int count))
                {
                    errorCounts.Remove(key);
                }
            }
        }

 

ok , 搞定

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章