前言
相信使用過WebApiThrottle的童鞋對AspNetCoreRateLimit應該不陌生,AspNetCoreRateLimit是一個ASP.NET Core速率限制的解決方案,旨在控制客戶端根據IP地址或客戶端ID向Web API或MVC應用發出的請求的速率。AspNetCoreRateLimit包含一個IpRateLimitMiddleware和ClientRateLimitMiddleware,每個中介軟體可以根據不同的場景配置限制允許IP或客戶端,自定義這些限制策略,也可以將限制策略應用在每個API URL或具體的HTTP Method上。
實踐
起初是因為新做的專案中,有天查詢日誌發現,對外的幾個公共介面經常被“惡意”呼叫,考慮到介面安全性問題,增加限流策略。
AspNetCoreRateLimit GayHub:https://github.com/stefanprodan/AspNetCoreRateLimit
根據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:https://github.com/stefanprodan/AspNetCoreRateLimit/wiki/IpRateLimitMiddleware#setup
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秒內可以訪問的次數是一次,當發生呼叫的時候會直接返回被限制的提示,而不能正常訪問介面,原因就是因為呼叫了兩次中介軟體。
最後
AspNetCoreRateLimit還可以根據客戶端ID進行配置策略,具體可以看一下官方的Wiki吧。