1. 程式人生 > >ASP.NET Core WebApi AspNetCoreRateLimit 限流中介軟體學習

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限制的規則放到資料庫中再推送到快取中。