ASP.NET Core WebApi AspNetCoreRateLimit 限流中介軟體學習
AspNetCoreRateLimit介紹:
AspNetCoreRateLimit是ASP.NET核心速率限制框架,能夠對WebApi,Mvc中控制限流,AspNetCoreRateLimit包包含IpRateLimit中介軟體和ClientRateLimit中介軟體,每個中介軟體都可以為不同的場景設定多個限,該框架的作者是stefanprodan,專案nuget地址是https://github.com/stefanprodan/AspNetCoreRateLimit。
對客戶端IP限流控制。
首先nuget安裝 Install-Package AspNetCoreRateLimit ,在Startup中Code以下程式碼,新增服務和注入,其中的配置是什麼;註釋都有了。
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); //新增appsettings.json services.AddOptions(); //需要儲存速率和ip規則 services.AddMemoryCache(); //載入appsettings.json中的配置項 ,下面三項是載入general,rules services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting")); services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies")); //注入計時器和規則 services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>(); services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>(); //新增框架服務 services.AddMvc(); }
在Configure中配置RateLimit的啟動
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env,ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug(); app.UseIpRateLimiting(); if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseMvc(); }
我們還需要再appsettings.json中寫入配置和規則:
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 100
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 1000
},
{
"Endpoint": "*",
"Period": "7d",
"Limit": 10000
}
]
}
如果EnableEndpointRateLimiting設定為false,那麼這些限制就全域性使用,例如,如果設定每秒5次呼叫的限制,對任何埠的任何http呼叫都計入這個限制,反之,如果它是true,那麼該限制將應用於{埠}{path}中的每個端點。
如果stackblockedrequest設定為false,則不會將拒絕呼叫新增到節流閥計數器,如果你要拒絕你必須設定為true;
ClientidHeader用於提取白清單的客戶端id,如果客戶端id在這個裡面,就不會應用速率限制。
覆蓋特定IP一般規則:
"IpRateLimitPolicies": {
"IpRules": [
{
"Ip": "84.247.85.224",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 200
}
]
},
{
"Ip": "192.168.3.22/25",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 5
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 150
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 500
}
]
}
]
}
IP欄位支援IP v4和v6值,我們還需要去定義速率限制規則
規則由端點,期間和限制組成,示例(將所有的端點的速率限制每秒2次呼叫),那麼定義如下:
{
"Endpoint": "*", "Period": "1s", "Limit": 2 }
如果在同一端點,例如get/values在一秒中你呼叫了3次,那麼第三次將會被阻止;但是如果說你在同一秒內還呼叫了Put/values那麼不會阻止,因為他們不是在同一端點之中。在期間(Period)中,還有單位 s m h 等.
有的時候我們對攔截有一定的自定義需求的時候,我們可以繼承IpRateLimitMiddleware,如以下定義:
public class CustomizationLimitMiddleware : IpRateLimitMiddleware
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
public CustomizationLimitMiddleware(RequestDelegate next, IOptions<IpRateLimitOptions> options, IRateLimitCounterStore counterStore, IIpPolicyStore policyStore, ILogger<IpRateLimitMiddleware> logger, IIpAddressParser ipParser = null) : base(next, options, counterStore, policyStore, logger, ipParser)
{
_options = options.Value;
_ipPolicyStore = policyStore;
}
public override ClientRequestIdentity SetIdentity(HttpContext httpContext)
{
var clientId = "anon";
if (httpContext.Request.Headers.Keys.Contains(_options.ClientIdHeader, StringComparer.CurrentCultureIgnoreCase))
{
clientId = httpContext.Request.Headers[_options.ClientIdHeader].First();
}
return new ClientRequestIdentity
{
Path = httpContext.Request.Path.ToString().ToLowerInvariant(),
HttpVerb = httpContext.Request.Method.ToLowerInvariant(),
ClientId = clientId
};
}
}
行為
當客戶端進行HTTP呼叫時,IpRateLimitMiddleware執行以下操作:
- 從請求體中獲取IP,客戶端IP,Http資訊,和一些URL,如果需要修改自己的提取邏輯,可以覆蓋IpRateLimitMiddleware.SetIdentity。
- 在白名單中搜索IP,客戶端ID和URL,如果有匹配則不執行任何操作
- 在IP規則中搜索匹配項,所有適用的規則按期間分組,對於每個期間使用最嚴格的規則
- 在匹配的一般規則中搜索,如果匹配的一般規則具有IP規則中不存在的定義時間段,則也使用此一般規則
- 對於每個匹配規則,速率限制計數器遞增,如果計數器值大於規則限制,則請求被阻止
如果請求被阻止,則客戶端會收到如下文字響應:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
如果請求沒有得到速率限制,那麼匹配規則中定義的最長週期用於組成X-Rate-Limit標頭,這些標頭將在響應中注入:
X-Rate-Limit-Limit: the rate limit period (eg. 1m, 12h, 1d)
X-Rate-Limit-Remaining: number of request remaining
X-Rate-Limit-Reset: UTC date time (ISO 8601) when the limits resets
預設情況下,組織了客戶端的呼叫我們都會記錄到日誌中,那麼我們可以使用Microsoft.Extensions.Logging.ILogger,這個就略過了。
我們有的時候需要新增ip規則或者更新速率,如一下所示:
public class IpRateLimitController : Controller
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore)
{
_options = optionsAccessor.Value;
_ipPolicyStore = ipPolicyStore;
}
[HttpGet]
public IpRateLimitPolicies Get()
{
return _ipPolicyStore.Get(_options.IpPolicyPrefix);
}
[HttpPost]
public void Post()
{
var pol = _ipPolicyStore.Get(_options.IpPolicyPrefix);
//add
pol.IpRules.Add(new IpRateLimitPolicy
{
Ip = "8.8.4.4",
Rules = new List<RateLimitRule>(new RateLimitRule[] {
new RateLimitRule {
Endpoint = "*:/api/testupdate",
Limit = 100,
Period = "1d" }
})
});
//update
_ipPolicyStore.Set(_options.IpPolicyPrefix, pol);
}
}
這樣呢,你可以將ip限制的規則放到資料庫中再推送到快取中。