netCore3.1 限制訪問頻率中間件 及 客戶端緩存

十年河東,十年河西,莫欺少年窮

學無止境,精益求精

客戶端緩存參考:https://www.cnblogs.com/catcher1994/p/responsecaching.html

接着Json web Token 中間件,今天寫了個簡單的限速中間件

json web token 中間件地址:https://www.cnblogs.com/chenwolong/p/16444022.html

所謂限速中間件,主要作用用於限制用戶頻繁訪問,防止多次高頻率請求造成服務器壓力大

下面演示一個每秒鐘同一個IP地址只能訪問一次帶有JWT授權的Action,一秒內多次訪問,直接返回 visits are too frequent

代碼如下:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using swapCommon;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace swap.Middlewares
{
    /// <summary>
    /// 限制用戶頻繁訪問帶有JwtToken授權 的 action中間件,一秒一次
    /// </summary>
    public class LimitActionMiddlewares
    {
        private readonly RequestDelegate next;
        private readonly IHostingEnvironment environment; 
        private readonly IMemoryCache  memory;
        private int milliSeconds = 1000;//一秒

        public LimitActionMiddlewares(RequestDelegate next, IHostingEnvironment environment , IMemoryCache memory)
        {
            this.next = next;
            this.environment = environment; 
            this.memory = memory;
        }

        public async Task Invoke(HttpContext context)
        {
            //正式環境限速
            if (environment.IsProduction())
            {
                var ip = context.Connection.RemoteIpAddress.ToString();
                string key = $"User_{ip}";
                long? lastVisit = memory.Get<long?>(key);
                //
                if (context.Items["userdata"] == null)
                {
                    await next.Invoke(context);
                }
                else
                {
                    if (lastVisit == null || Environment.TickCount64 - lastVisit > milliSeconds)
                    {
                        memory.Set<long>(key, Environment.TickCount64, TimeSpan.FromSeconds(10));//避免長期不訪問的用戶佔用服務器資源 
                        await next.Invoke(context);
                    }
                    else
                    {
                        await Response(context, 429, "visits are too frequent");
                    }
                }
            }
            else
            {
                await next.Invoke(context);
            }
            
        }

        private async Task Response(HttpContext httpContext, int statusCode, string message)
        {
            httpContext.Response.StatusCode = statusCode;
            httpContext.Response.ContentType = "application/json; charset=utf-8";
            var result = CommonBaseResponse.SetResponse(false, message);
            await httpContext.Response.WriteAsync(JsonConvert.SerializeObject(result));
        }
    }
}

服務註冊時需引入緩存中間件

 public void ConfigureServices(IServiceCollection services)
        { 
            services.AddResponseCaching(); 
            //其他代碼
          }

引用中間件時

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            } 
            //注意中間件的引入順序,jwt在前,limitAction在後
            app.UseMiddleware<JwtMiddlewares>();
            app.UseMiddleware<LimitActionMiddlewares>();
            app.UseMiddleware<ExceptionMiddlewares>();
          }
注意中間件的引入順序,jwt在前,limitAction在後

代碼解讀

 if(context.Items["userdata"] == null)
            { 
                await next.Invoke(context);
            }

 context.Items["userdata"] 是通過Jwt中間件進行賦值了,在一次請求中,它可以在各個中間件之間傳遞。

本中間件作用於需要JWT授權的Action訪問,因此需要上述 if 判斷

 

 更深次的原因是:

 由上圖可知,一次請求,每個中間件都會被執行兩次,兩次執行過程所用時間小於1秒,如果不加上述 if 判斷,在swagger初始化時,會報visits are too frequent 異常。

請求示例

 

 

 

 @陳臥龍的博客

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章