开发随笔记录——ASP.NET Core自定义中间件通过本地缓存实现客户端IP访问限流
声明:本文章中框架使用ASP.NET Core 3.1
首先,在appsettings.json中配置该中间件中需要使用的配置信息,如下:
"RateLimiting": { "Count": 5, //限定时间内能够访问的次数 "Seconds": 60 //达到访问上限后多少秒后可以重新访问 }
其次,创建RequestRateLimitingMiddleware实现类,代码如下:
public class RequestRateLimitingMiddleware { //限定时间内能够访问的次数 private readonly int Limit; //达到访问上限后多少秒后可以重新访问 private readonly int Seconds; private readonly RequestDelegate next; private readonly IMemoryCache requestStore; public RequestRateLimitingMiddleware(RequestDelegate next, IConfiguration Configuration, IMemoryCache requestStore) { this.next = next; this.requestStore = requestStore; Limit = Configuration.GetValue<int>("RateLimiting:Count"); Seconds = Configuration.GetValue<int>("RateLimiting:Seconds"); } public async Task Invoke(HttpContext context) { var requestKey = $"{context.Connection.RemoteIpAddress.MapToIPv4()}-{context.Request.Method}-{context.Request.Path}"; int hitCount = 0; var cacheOptions = new MemoryCacheEntryOptions() { AbsoluteExpiration = DateTime.Now.AddSeconds(Seconds) }; if (requestStore.TryGetValue(requestKey, out hitCount)) { if (hitCount < Limit) { await ProcessRequest(context, requestKey, hitCount, cacheOptions); } else { context.Response.Headers["X-RateLimit-RetryAfter"] = cacheOptions.AbsoluteExpiration?.ToString("yyyy-MM-dd HH:mm:ss"); context.Response.StatusCode = StatusCodes.Status429TooManyRequests; } } else { await ProcessRequest(context, requestKey, hitCount, cacheOptions); } } private async Task ProcessRequest(HttpContext context,string requestKey,int hitCount,MemoryCacheEntryOptions cacheOptions) { hitCount++; requestStore.Set(requestKey, hitCount, cacheOptions); context.Response.Headers["X-RateLimit-Limit"] = Limit.ToString(); context.Response.Headers["X-RateLimit-Remaining"] = (Limit - hitCount).ToString(); await next(context); } }
稍后,在Startup.cs文件中,对自定义的中间件进行注册使用,如下:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseAuthorization(); app.UseMiddleware<RequestRateLimitingMiddleware>(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
最后,就可以进行测试了,测试结果对比如下:
提示:X-RateLimit-Limit代表在限定时间内可访问次数上限。
X-RateLimit-Remaining代表在限定时间内已经访问了多少次。
X-RateLimit-RetryAfter代表什么时间后可再次重新进行访问。