簡單的限流過濾器

风中起舞發表於2024-06-12

API介面都是提供給第三方服務/客戶端呼叫,所有請求地址以及請求引數都是暴露給使用者的。

每次請求一個HTTP請求,使用者都可以透過F12,或者抓包工具看到請求的URL連結,然後copy出來。這樣是非常不安全的,有人可能會惡意的刷我們的介面,那這時該怎麼辦呢?

增加一個全域性過濾器 獲取客戶端的IP 限制固定時間內的訪問次數即可

第一步:建立全域性過濾器 RateLimitFilter

 public class RateLimitFilter : ActionFilterAttribute
    {
        private const int MaxRequests = 30; //1分鐘訪問最大頻率
        private bool StartUp = true; //是否啟用
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (StartUp)
            {
                base.OnActionExecuting(context);
                string clientId = GetIP();
                if (GetCache(clientId) == null)
                {
                    SetCacheRelativeTime(clientId, 1, 60);
                }
                else
                {
                    var cs = int.Parse(GetCache(clientId).ToString());
                    SetCacheRelativeTime(clientId, cs += 1, 60);
                }
                //var x = int.Parse(GetCache(clientId).ToString());
                if (int.Parse(GetCache(clientId).ToString()) > MaxRequests)
                {
                    //返回值規範不統一
                    context.Result = new ContentResult { Content = "<script type='text/javascript'>alert('" + clientId + "  訪問過於頻繁,請稍等片刻!');</script><h1 style='text-align: center; color: red;'>" + clientId + "  訪問過於頻繁,請稍等片刻!<h1>" };

                    //返回值規範統一   前端有錯誤提示
                    //context.Result = new JsonResult()
                    //{
                    //    Data = new { Result = false, status = false, suc = false, message = "" + clientId + "  訪問過於頻繁,請稍等片刻!" },
                    //    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                    //};
                }
            } 
        }


        /// <summary>
        /// 獲取客戶端IP地址
        /// </summary>
        /// <returns>若失敗則返回回送地址</returns>
        public static string GetIP()
        {
            //如果客戶端使用了代理伺服器,則利用HTTP_X_FORWARDED_FOR找到客戶端IP地址
            string userHostAddress = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            if (!string.IsNullOrEmpty(userHostAddress))
            {
                userHostAddress = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"].ToString().Split(',')[0].Trim();
            }
            //否則直接讀取REMOTE_ADDR獲取客戶端IP地址
            if (string.IsNullOrEmpty(userHostAddress))
            {
                userHostAddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
            }
            //前兩者均失敗,則利用Request.UserHostAddress屬性獲取IP地址,但此時無法確定該IP是客戶端IP還是代理IP
            if (string.IsNullOrEmpty(userHostAddress))
            {
                userHostAddress = HttpContext.Current.Request.UserHostAddress;
            }
            //最後判斷獲取是否成功,並檢查IP地址的格式(檢查其格式非常重要)
            if (!string.IsNullOrEmpty(userHostAddress) && IsIP(userHostAddress))
            {
                return userHostAddress;
            }
            return "127.0.0.1";
        }

        /// <summary>
        /// 檢查IP地址格式
        /// </summary>
        /// <param name="ip"></param>
        /// <returns></returns>
        public static bool IsIP(string ip)
        {
            return System.Text.RegularExpressions.Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
        }

        #region  設定相對過期時間Cache值(即:訪問啟用後不過期)
        /// <summary>        
        /// 設定相對過期時間Cache值(即:訪問啟用後不過期)
        /// </summary>        
        /// <param name="objectkey"></param>        
        /// <param name="objObject"></param>        
        /// <param name="timeSpan">超過多少時間不呼叫就失效,單位是秒</param>        

        public static void SetCacheRelativeTime(string objectkey, object objObject, int timeSpan)
        {
            System.Web.Caching.Cache objCache = HttpRuntime.Cache;
            objCache.Insert(objectkey, objObject, null, DateTime.MaxValue, TimeSpan.FromSeconds(timeSpan));
        }
        #endregion

        #region  獲取當前應用程式指定CacheKey的Cache值
        /// <summary>
        /// 獲取當前應用程式指定CacheKey的Cache值
        /// </summary>
        /// <param name="CacheKey"></param>
        /// <returns></returns>y
        public static object GetCache(string CacheKey)
        {
            try
            {
                System.Web.Caching.Cache objCache = HttpRuntime.Cache;
                Object value = objCache[CacheKey];
                if (value != null)
                {
                    return value;
                }
                else
                {
                    return null;
                }
            }
            catch (Exception)
            {
                return null;
            }

        }
        #endregion
    }

第二步:FilterConfig類並註冊你的全域性過濾器

 public class FilterConfig
 {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new RateLimitFilter()); // 過濾器
        }
 }

第三步:Global.asax 檔案中註冊全域性過濾器

protected void Application_Start()
{
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            UnityConfig.RegisterComponents();

            // 註冊全域性過濾器
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);

}

相關文章