翼度科技»论坛 编程开发 .net 查看内容

白嫖一个WebAPI限流解决方案

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
什么是API限流:
API 限流是限制用户在一定时间内 API 请求数量的过程。应用程序编程接口 (API) 充当用户和软件应用程序之间的网关。例如,当用户单击社交媒体上的发布按钮时,点击该按钮会触发 API 调用。此 API 与社交媒体应用程序的网络服务器进行交互,并执行发布操作。此用户可以是人,也可以是其他软件应用程序。
为什么要限流:
API 是组织最大的资产之一。API 可帮助网站或移动应用程序的用户完成任务。随着用户数量的增加,网站或移动应用程序开始出现性能下降的迹象。因此,拥有更好连接或更快界面的用户可能会获得比其他用户更好的体验。API 限流是一种巧妙的解决方案,可帮助组织确保其 API 的合理使用。
API 限流还有助于抵御拒绝服务 (DoS) 攻击,在 DoS 攻击中,恶意用户发送大量请求以使网站或移动应用程序崩溃。随着在线用户数量的增加,企业需要实施 API 限流机制,以确保公平使用、数据安全并防止恶意攻击。
API限流的原理:
虽然 API 限流有多种算法,但以下是所有 API 限流算法的基本步骤:
1.客户端/用户调用与网络服务或应用程序交互的 API。
2.API 限流逻辑会检查当前请求是否超过允许的 API 调用次数。
3.如果请求在限制范围内,API 将照常执行并完成用户的任务。
4.如果请求超出限制,API 会向用户返回错误响应。
5.用户必须等待预先约定的时间段,或者付费才能进行更多的 API 调用。
这里有篇文章介绍很全面,可以看一看《API 限流技术探索与实践
这个限流方案也是在百度收集整理而来,我这里采取的是滑动算法:
我们需要准备几个类:
1.ApiAuthorize类
ApiAuthorize继承于IAuthorizationFilter(授权过滤器),和IAuthorizationFilter相同的还有其他三种过滤器,合起来称为四大过滤器,
另外三个分别是IResourceFilter资源过滤器(缓存接口的数据),IActionFilter动作过滤器(记录操作日志),IExceptionFilter(错误过滤器)
IAuthorizationFilter
  1. public class CtmAuthorizationFilterAttribute : Attribute, IAuthorizationFilter
  2. {
  3.      public void OnAuthorization(AuthorizationFilterContext context)
  4.      {
  5.          // context.HttpContext.User.Claims
  6.          context.HttpContext.Items["User"] = "HuangMing";
  7.          System.Console.WriteLine("OnAuthorization");
  8.      }
  9. }
复制代码
View CodeIResourceFilter
  1. //Program.cs中注册缓存:
  2. builder.Services.AddSingleton<IMemoryCache,MemoryCache>();
  3. builder.Services.AddSingleton<IDistributedCache, MemoryDistributedCache>();
  4. var app = builder.Build();
  5. public class CtmResourceFilterAttribute : Attribute, IResourceFilter
  6. {
  7.     private readonly IMemoryCache _cache;
  8.     public CtmResourceFilterAttribute(IMemoryCache cache)
  9.     {
  10.         this._cache = cache;
  11.     }
  12.     public void OnResourceExecuted(ResourceExecutedContext context)
  13.     {
  14.         var path = context.HttpContext.Request.Path.ToString();
  15.         if (context.Result != null)
  16.         {
  17.             var value =  (context.Result as ObjectResult).Value.ToString();
  18.             _cache.Set(path, value,TimeSpan.FromHours(1));
  19.         }
  20.     }
  21.     public void OnResourceExecuting(ResourceExecutingContext context)
  22.     {
  23.         var path = context.HttpContext.Request.Path.ToString();
  24.         var hasValue = _cache.TryGetValue(path, out object value);
  25.         if (hasValue)
  26.         {
  27.             context.Result = new ContentResult
  28.             {
  29.                 Content = value.ToString()
  30.             };
  31.         }
  32.     }
  33. }
复制代码
View CodeIActionFilter
  1. public class CtmActionFilterAttribute : Attribute, IActionFilter
  2. {
  3.     public void OnActionExecuted(ActionExecutedContext context)
  4.     {
  5.     }
  6.     public void OnActionExecuting(ActionExecutingContext context)
  7.     {
  8.         //从serviceProvider中获取Logger服务
  9.         var logger =  context.HttpContext.RequestServices.GetService<ILogger<CtmActionFilterAttribute>>();
  10.         //获取路由地址
  11.         var path = context.HttpContext.Request.Path;
  12.         //从RouteData字典中获取控制器名称
  13.         var controller = context.RouteData.Values["controller"];
  14.         //从RouteData字典中获取动作名称
  15.         var action = context.RouteData.Values["action"];
  16.         //从ActionArguments中获取接口参数
  17.         var arguments = string.Join(",", context.ActionArguments);
  18.         logger.LogInformation($"访问的路由:{path},控制器是{controller},行为是{action},参数是{arguments}");
  19.     }
  20. }
  21. //当过滤器中需要使用依赖注入时,在使用属性标注时,需要使用如下方式:
  22. 1.属性标注
  23. [TypeFilter(typeof(CtmActionFilterAttribute))]
  24. 2.从容器中获取服务
  25. var logger =  context.HttpContext.RequestServices.GetService<ILogger<CtmActionFilterAttribute>>();
复制代码
View CodeIActionFilter
  1. public class CtmExceptionFilterAttribute : Attribute, IExceptionFilter
  2. {
  3.     public void OnException(ExceptionContext context)
  4.     {
  5.         context.Result = new ContentResult{
  6.             Content =context.Exception.Message
  7.         };
  8.     }
  9. }
复制代码
View Code现在编写自己的项目代码
ApiAuthorize
  1. public class ApiAuthorize : IAuthorizationFilter
  2.     {
  3.         public async void OnAuthorization(AuthorizationFilterContext context)
  4.          {
  5.             if (context.Filters.Contains(new MyNoAuthentication()))
  6.             {
  7.                 return;
  8.             }
  9.            
  10.             #region 用户请求限流
  11.             {
  12.                 string ip = context.HttpContext.Connection.RemoteIpAddress.ToString();
  13.                 var cotrollaction = context.ActionDescriptor;
  14.                 string action = cotrollaction.RouteValues["action"].ToString();
  15.                 string controller = cotrollaction.RouteValues["controller"].ToString();
  16.                 if (string.IsNullOrWhiteSpace(ip) || string.IsNullOrWhiteSpace(controller) || string.IsNullOrWhiteSpace(action))
  17.                 {
  18.                     context.Result = new JsonResult("系统正忙,请稍微再试!");
  19.                     return;
  20.                 }
  21.                 ip = ip + ":" + controller + ":" + action;
  22.                 IPCacheInfoModel ipModel = IPCacheHelper.GetIPLimitInfo(ip);
  23.                 if (!ipModel.IsVisit)
  24.                 {
  25.                     context.Result = new JsonResult("系统正忙,请稍微再试!");
  26.                     return;
  27.                 }
  28.                 string ACting = controller + ":" + action;
  29.                 IPCacheInfoModel ipModel2 = IPCacheHelper.GetIPLimitInfo(ACting);
  30.             }
  31.             #endregion
  32.             
  33.             #endregion
  34.         }
  35.     }
复制代码
View Code然后编写 MyAuthentication类
MyAuthentication
  1. /// <summary>
  2. /// 构造引用
  3. /// </summary>
  4.     public class MyAuthentication : Attribute, IFilterMetadata
  5.     {
  6.     }
  7.     public class MyNoAuthentication : Attribute, IFilterMetadata
  8.     {
  9.     }
复制代码
View Code以上两个可以做限流也能做鉴权,数据签名认证等
如果需要限流,我们还需要三个类:
IPActionFilterAttribute 信息返回类
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Net;
  4. using System.Net.Http;
  5. using System.Threading.Tasks;
  6. using System.Web.Http.Controllers;
  7. using System.Web.Http.Filters;
  8. namespace EvaluationSystem.XLAction
  9. {
  10.     /// <summary>
  11.     /// 限制单个IP短时间内访问次数
  12.     /// </summary>
  13.     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
  14.     public class IPActionFilterAttribute : ActionFilterAttribute
  15.     {
  16.         /// <summary>
  17.         /// 限制单个IP短时间内访问次数
  18.         /// </summary>
  19.         /// <param name="actionContext"></param>
  20.         public override void OnActionExecuting(HttpActionContext actionContext)
  21.         {
  22.             string ip = actionContext.Request.ToString();
  23.             IPCacheInfoModel ipModel = IPCacheHelper.GetIPLimitInfo(ip);
  24.             if (!ipModel.IsVisit)
  25.             {
  26.                 // Logger.Warn(string.Format("IP【{0}】被限制了【{1}】次数", ipModel.IP, ipModel.Limit));
  27.                 actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, "系统正忙,请稍微再试。");
  28.                 return;
  29.             }
  30.             base.OnActionExecuting(actionContext);
  31.         }
  32.     }
  33. }
复制代码
View CodeIPCacheHelper 请求记录类
  1. using EvaluationSystem.HelpTool;
  2. using EvaluationSystem.HelpTool.GetSYSValue;
  3. using System;
  4. using System.Collections.Generic;
  5. namespace EvaluationSystem.XLAction
  6. {
  7.     /// <summary>
  8.     /// 限制单个IP访问次数
  9.     /// </summary>
  10.     public class IPCacheHelper
  11.     {
  12.         /// <summary>         
  13.         /// IP缓存集合         
  14.         /// </summary>      
  15.         private static List<IPCacheInfoModel> dataList = new List<IPCacheInfoModel>();
  16.         private static object lockObj = new object();
  17.         //SQLHelp ht = new SQLHelp();
  18.         public static string  maxTimes1 = GetConfig.GetConfiguration("XLAction:maxTimes");
  19.         public static string partSecond1 = GetConfig.GetConfiguration("XLAction:partSecond");
  20.         /// <summary>        
  21.         /// 一段时间内,最大请求次数,必须大于等于1
  22.         ///</summary>
  23.         private static int maxTimes = Convert.ToInt32(string.IsNullOrWhiteSpace(maxTimes1)? "0":maxTimes1);
  24.         /// <summary>  
  25.         /// 一段时间长度(单位秒),必须大于等于1     
  26.         /// </summary>
  27.         private static int partSecond = Convert.ToInt32(string.IsNullOrWhiteSpace(partSecond1) ? "0" : partSecond1);
  28.         /// <summary>  
  29.         /// 请求被拒绝是否加入请求次数   
  30.         /// </summary>  
  31.         private static bool isFailAddIn = false;
  32.         static IPCacheHelper()
  33.         {
  34.         }
  35.         /// <summary>      
  36.         /// 设置时间,默认maxTimes=3, partSecond=30         
  37.         /// </summary>        
  38.         /// <param name="_maxTimes">最大请求次数</param>        
  39.         /// <param name="_partSecond">请求单位时间</param>         
  40.         public static void SetTime(int _maxTimes, int _partSecond)
  41.         {
  42.             maxTimes = _maxTimes;
  43.             partSecond = _partSecond;
  44.         }
  45.         /// <summary>   
  46.         /// 检测一段时间内,IP的请求次数是否可以继续请求和使用  
  47.         /// </summary>        
  48.         /// <param name="ip">ip</param>   
  49.         /// <returns></returns>      
  50.         public static bool CheckIsAble(string ip)
  51.         {
  52.             lock (lockObj)
  53.             {
  54.                 var item = dataList.Find(p => p.IP == ip);
  55.                 if (item == null)
  56.                 {
  57.                     item = new IPCacheInfoModel();
  58.                     item.IP = ip;
  59.                     item.ReqTime.Add(DateTime.Now);
  60.                     dataList.Add(item);
  61.                     return true;
  62.                 }
  63.                 else
  64.                 {
  65.                     if (item.ReqTime.Count > maxTimes)
  66.                     {
  67.                         item.ReqTime.RemoveAt(0);
  68.                     }
  69.                     var nowTime = DateTime.Now;
  70.                     if (isFailAddIn)
  71.                     {
  72.                         #region 请求被拒绝也需要加入当次请求
  73.                         item.ReqTime.Add(nowTime);
  74.                         if (item.ReqTime.Count >= maxTimes)
  75.                         {
  76.                             if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
  77.                             {
  78.                                 return false;
  79.                             }
  80.                             else
  81.                             {
  82.                                 return true;
  83.                             }
  84.                         }
  85.                         else
  86.                         {
  87.                             return true;
  88.                         }
  89.                         #endregion
  90.                     }
  91.                     else
  92.                     {
  93.                         #region 请求被拒绝就不需要加入当次请求了
  94.                         if (item.ReqTime.Count >= maxTimes)
  95.                         {
  96.                             if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
  97.                             {
  98.                                 return false;
  99.                             }
  100.                             else
  101.                             {
  102.                                 item.ReqTime.Add(nowTime);
  103.                                 return true;
  104.                             }
  105.                         }
  106.                         else
  107.                         {
  108.                             item.ReqTime.Add(nowTime);
  109.                             return true;
  110.                         }
  111.                         #endregion
  112.                     }
  113.                 }
  114.             }
  115.         }
  116.         /// <summary>   
  117.         /// 检测一段时间内,IP的请求次数是否可以继续请求和使用  
  118.         /// </summary>        
  119.         /// <param name="ip">ip</param>   
  120.         /// <returns></returns>      
  121.         public static IPCacheInfoModel GetIPLimitInfo(string ip)
  122.         {
  123.             lock (lockObj)
  124.             {
  125.                 var item = dataList.Find(p => p.IP == ip);
  126.                 if (item == null) //IP开始访问
  127.                 {
  128.                     item = new IPCacheInfoModel();
  129.                     item.IP = ip;
  130.                     item.ReqTime.Add(DateTime.Now);
  131.                     dataList.Add(item);
  132.                     item.IsVisit = true; //可以继续访问
  133.                     return item;
  134.                 }
  135.                 else
  136.                 {
  137.                     if (item.ReqTime.Count > maxTimes)
  138.                     {
  139.                         item.ReqTime.RemoveAt(0);
  140.                     }
  141.                     var nowTime = DateTime.Now;
  142.                     if (isFailAddIn)
  143.                     {
  144.                         #region 请求被拒绝也需要加入当次请求
  145.                         item.ReqTime.Add(nowTime);
  146.                         if (item.ReqTime.Count >= maxTimes)
  147.                         {
  148.                             if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
  149.                             {
  150.                                 item.Limit++; //限制次数+1
  151.                                 item.IsVisit = false;//不能继续访问
  152.                                 return item;
  153.                             }
  154.                             else
  155.                             {
  156.                                 item.IsVisit = true; //可以继续访问
  157.                                 return item; //单个IP30秒内 没有多次访问
  158.                             }
  159.                         }
  160.                         else
  161.                         {
  162.                             item.IsVisit = true; //可以继续访问
  163.                             return item; //单个IP访问次数没有达到max次数
  164.                         }
  165.                         #endregion
  166.                     }
  167.                     else
  168.                     {
  169.                         #region 请求被拒绝就不需要加入当次请求了
  170.                         if (item.ReqTime.Count >= maxTimes)
  171.                         {
  172.                             if (item.ReqTime[0].AddSeconds(partSecond) > nowTime)
  173.                             {
  174.                                 item.Limit++; //限制次数+1
  175.                                 item.IsVisit = false;//不能继续访问
  176.                                 return item;
  177.                             }
  178.                             else
  179.                             {
  180.                                 item.ReqTime.Add(nowTime);
  181.                                 item.IsVisit = true; //可以继续访问
  182.                                 return item;
  183.                             }
  184.                         }
  185.                         else
  186.                         {
  187.                             item.ReqTime.Add(nowTime);
  188.                             item.IsVisit = true; //可以继续访问
  189.                             return item;
  190.                         }
  191.                         #endregion
  192.                     }
  193.                 }
  194.             }
  195.         }
  196.     }
  197. }
复制代码
View CodeIPCacheInfoModel 实体类
  1. using System;
  2. using System.Collections.Generic;
  3. namespace EvaluationSystem.XLAction
  4. {
  5.     public class IPCacheInfoModel
  6.     {
  7.         /// <summary>
  8.         /// IP
  9.         /// </summary>
  10.         public string IP { get; set; }
  11.         /// <summary>
  12.         /// 限制次数
  13.         /// </summary>
  14.         public int Limit { get; set; }
  15.         /// <summary>
  16.         /// 是否可以访问
  17.         /// </summary>
  18.         public bool IsVisit { get; set; }
  19.         /// <summary>
  20.         /// 访问时间
  21.         /// </summary>
  22.         private List<DateTime> reqTime = new List<DateTime>();
  23.         /// <summary>
  24.         /// 访问时间
  25.         /// </summary>
  26.         public List<DateTime> ReqTime
  27.         {
  28.             get { return this.reqTime; }
  29.             set { this.reqTime = value; }
  30.         }
  31.     }
  32. }
复制代码
View Code时间按秒算
        private static int maxTimes ;
请求次数
        private static int partSecond ;
为了方便控制,不去修改我们的API程序,可以将这两个信息配置进appsettings.json文件里面
  "XLAction": {//请求限流 秒钟一次
    "maxTimes": "1",
    "partSecond": "1"
  }
为了获取appsettings.json来买你的信息,我们需要一个方法拿到json里面的信息
GetConfiguration
  1.     public class GetConfig
  2.     {
  3.         public static string GetConfiguration(string configKey)
  4.         {
  5.             var builder = new ConfigurationBuilder()
  6.                 .SetBasePath(Directory.GetCurrentDirectory())
  7.                 .AddJsonFile("appsettings.json");
  8.             var config = builder.Build();
  9.             if (configKey.Contains(":"))
  10.             {
  11.                 return config.GetSection(configKey).Value;//获取分级参数值
  12.             }
  13.             else
  14.             {
  15.                 return config[configKey];//获取直级参数值
  16.             }
  17.             //youdianwenti w xiangxiang
  18.         }
  19.     }
复制代码
View Code以上工作准备完全后,在我们的Startup里面修改加入以下代码
如果有ConfigureServices类,添加如下
            //注册guolv
            services.AddControllers(o =>
            {
                o.Filters.Add();
                o.Filters.Add();
                //o.Filters.Add(typeof(BasicAuthAttribute));
                //services.AddJwtEx();//这里就是注入JWT
            });
 
如果不是 如下添加
builder.Services.AddMvc(options => options.Filters.Add(new AuthorizeFilter()));
//注册guolv
builder.Services.AddControllers(o =>
{
    o.Filters.Add();
    o.Filters.Add();
});
然后就大功告成
现在直接看结果

 
 
接着频繁操作

 
 该方案来自网络加以修改,如有侵权,请联系删除
 

来源:https://www.cnblogs.com/libo962464/archive/2023/02/14/17120659.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具