前言
相信使用过 webApiThrottle 的童鞋对 AspNetCoreRateLimit 应该不陌生, AspNetCoreRateLimit 是一个 ASP.NET Core 速率限制的解决方案, 旨在控制客户端根据 IP 地址或客户端 ID 向 Web API 或 MVC 应用发出的请求的速率. AspNetCoreRateLimit 包含一个 IpRateLimitMiddleware 和 ClientRateLimitMiddleware, 每个中间件可以根据不同的场景配置限制允许 IP 或客户端, 自定义这些限制策略, 也可以将限制策略应用在每个 API URL 或具体的 HTTP Method 上.
实践
起初是因为新做的项目中, 有天查询日志发现, 对外的几个公共接口经常被 "恶意" 调用, 考虑到接口安全性问题, 增加限流策略.
AspNetCoreRateLimit GayHub:
根据 IP 进行限流
通过 nuget 安装 AspNetCoreRateLimit, 当前版本是 3.0.5, 因为实际项目中用的都是分布式缓存, 在这里不用内存存储, 而是结合 Redis 进行使用, 内存存储直接参考官方的 Wiki 就可以了.
- Install-Package AspNetCoreRateLimit
- Install-Package Microsoft.Extensions.Caching.Redis
在 Startup.ConfigureServices 中将服务和其他依赖注入
- public void ConfigureServices(IServiceCollection services)
- {
- #region MVC
- services.AddMvc(
- options =>
- {
- options.UseCentralRoutePrefix(new RouteAttribute("api/"));
- }
- ).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
- #endregion
- services.AddDistributedRedisCache(options =>
- {
- options.Configuration = "127.0.0.1:6379,password=123456,connectTimeout=5000,syncTimeout=10000";
- options.InstanceName = "WebRatelimit";
- });
- // 加载配置
- services.AddOptions();
- // 从 appsettings.JSON 获取相应配置
- services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
- // 注入计数器和规则存储
- services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
- services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
- services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
- // 配置 (计数器密钥生成器)
- services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
- }
在 Startup.Configure 启用
- // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- else
- {
- App.UseHsts();
- }
- // 启用限流, 需在 UseMvc 前面
- App.UseIpRateLimiting();
- App.UseMvc();
- }
为了不影响 appsettings.JSON 的美观吧, 可以新建一个 RateLimitConfig.JSON, 并 Program 中启动加载中增加
- public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
- WebHost.CreateDefaultBuilder(args)
- .UseStartup<Startup>().ConfigureAppConfiguration((host,config)=>
- {
- config.AddJsonFile($"RateLimitConfig.json", optional: true, reloadOnChange: true);
- });
RateLimitConfig.JSON 配置如下:
- {
- "IpRateLimiting": {
- //false 则全局将应用限制, 并且仅应用具有作为端点的规则 * . true 则限制将应用于每个端点, 如 {HTTP_Verb}{PATH}
- "EnableEndpointRateLimiting": true,
- //false 则拒绝的 API 调用不会添加到调用次数计数器上
- "StackBlockedRequests": false,
- "RealIpHeader": "X-Real-IP",
- "ClientIdHeader": "X-ClientId",
- "HttpStatusCode": 200,
- "QuotaExceededResponse": {
- "Content": "{{\"code\":429,\"msg\":\" 访问过于频繁, 请稍后重试 \",\"data\":null}}",
- "ContentType": "application/json",
- "StatusCode": 200
- },
- "IpWhitelist": [ ],
- "EndpointWhitelist": [],
- "ClientWhitelist": [],
- "GeneralRules": [
- {
- "Endpoint": "*:/api/values/test",
- "Period": "5s",
- "Limit": 3
- }
- ]
- }
- }
重要配置说明:
QuotaExceededResponse 是自定义返回的内容, 所以必须设置 HttpStatusCode 和 StatusCode 为 200.
GeneralRules 是具体的策略, 根据不同需求配置不同端点即可, Period 的单位可以是 s, m, h, d,Limint 是单位时间内的允许访问的次数;
IpWhitelist 是 IP 白名单, 本地调试或者 UAT 环境, 可以加入相应的 IP, 略过策略的限制;
EndpointWhitelist 是端点白名单, 如果全局配置了访问策略, 设置端点白名单相当于 IP 白名单一样, 略过策略的限制;
其他配置项请参考 Wiki:
Fiddler 开始测试
测试接口: http://127.0.0.1:5000/api/values/Test
- [HttpGet]
- public object test()
- {
- return "ok";
- }
调用结果:
调用次数和剩余调用次数在 Head 可以看到,(吃我一个链接: https://www.cnblogs.com/EminemJK/p/12720691.html)
如果调用超过策略后, 调用失败, 返回我们自定义的内容
在 Redis 客户端可以看到策略的一些情况,
其他
通常在项目中, Authorization 授权是少不了了, 加入限流后, 在被限流的接口调用后, 限流拦截器使得跨域策略失效, 故重写拦截器中间件, 继承 IpRateLimitMiddleware 即可:
- public class IPLimitMiddleware : IpRateLimitMiddleware
- {
- public IPLimitMiddleware(RequestDelegate next, IOptions<IpRateLimitOptions> options, IRateLimitCounterStore counterStore, IIpPolicyStore policyStore, IRateLimitConfiguration config, ILogger<IpRateLimitMiddleware> logger)
- : base(next, options, counterStore, policyStore, config, logger)
- {
- }
- public override Task ReturnQuotaExceededResponse(HttpContext httpContext, RateLimitRule rule, string retryAfter)
- {
- httpContext.Response.Headers.Append("Access-Control-Allow-Origin", "*");
- return base.ReturnQuotaExceededResponse(httpContext, rule, retryAfter);
- }
- }
然后修改 Startup.Configure,
- // 启用限流, 需在 UseMvc 前面
- //App.UseIpRateLimiting();
- App.UseMiddleware<IPLimitMiddleware>();
- App.UseMvc();
特别需要注意的坑是, 在其他文章的教程中, 他们会写成:
App.UseMiddleware<IPLimitMiddleware>().UseIpRateLimiting();// 错误的演示 https://www.cnblogs.com/EminemJK/p/12720691.html
这些写你测试的时候会发现,
X-Rate-Limit-Remaining 递减量会变成 2, 也不是递减 1, 举栗子, 配置如下:
- "Endpoint": "*:/api/values/test",
- "Period": "3s",
- "Limit": 1
表示 3 秒内可以访问的次数是 1 一次, 当发生调用的时候会直接返回被限制的提示, 而不能正常访问接口.
最后
AspNetCoreRateLimit 还可以根据客户端 ID 进行配置策略, 具体可以看一下官方的 Wiki 吧.
来源: https://www.cnblogs.com/EminemJK/p/12720691.html