速率限制中间件AspNetCoreRateLimit

AspNetCoreRateLimit

AspNetCoreRateLimit 是一种 ASP.NET Core 速率限制解决方案,旨在控制客户端可以根据 IP 地址或客户端 ID 向 Web API 或 MVC 应用发出的请求速率。AspNetCoreRateLimit 包包含一个 IpRateLimitMiddleware 和一个 ClientRateLimitMiddleware,对于每个中间件,你可以为不同的场景设置多个限制,例如允许 IP 或客户端在每秒、15 分钟等时间间隔内进行最大调用次数。您可以定义这些限制以解决对 API 发出的所有请求,也可以将限制范围限定为每个 API URL 或 HTTP 谓词和路径。

设置

NuGet 安装:NuGet install

Install-Package AspNetCoreRateLimit

Install-Package AspNetCoreRateLimit.Redis

Startup.cs代码

public void ConfigureServices(IServiceCollection services)
{
	// needed to load configuration from appsettings.json
	services.AddOptions();

	// needed to store rate limit counters and ip rules
	services.AddMemoryCache();

	//load general configuration from appsettings.json
	services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));

	//load ip rules from appsettings.json
	services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));

	// inject counter and rules stores
	services.AddInMemoryRateLimiting();
        //services.AddDistributedRateLimiting<AsyncKeyLockProcessingStrategy>();
        //services.AddDistributedRateLimiting<RedisProcessingStrategy>();
        //services.AddRedisRateLimiting();

	// Add framework services.
	services.AddMvc();

        // configuration (resolvers, counter key builders)
        services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	app.UseIpRateLimiting();

	app.UseMvc();
}

您应该在任何其他组件之前注册中间件。

如果对应用进行负载均衡,则需要与 Redis 或 SQLServer 一起使用,以便所有 kestrel 实例都具有相同的速率限制存储。 您应该像这样注入分布式存储,而不是内存中的存储:IDistributedCache

// inject counter and rules distributed cache stores
services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();

配置和一般规则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
      }
    ]
  }

如果设置为,则限制将全局应用,并且仅应用具有终结点的规则。例如,如果设置了每秒 5 次调用的限制,则对任何终结点的任何 HTTP 调用都将计入该限制。EnableEndpointRateLimitingfalse*

如果设置为 ,则限制将应用于每个端点,如 中所示。例如,如果设置为每秒 5 次调用的限制,则客户端可以每秒调用 5 次,但也可以调用 5 次。EnableEndpointRateLimitingtrue{HTTP_Verb}{PATH}*:/api/valuesGET /api/valuesPUT /api/values

如果设置为 ,则拒绝的呼叫不会添加到限制计数器中。如果客户端每秒发出 3 个请求,并且您设置了每秒 <> 个呼叫的限制,则其他限制(如每分钟或每天计数器)将仅记录第一个呼叫,即未被阻止的呼叫。如果您希望被拒绝的请求计入其他限制,则必须设置为 。StackBlockedRequestsfalseStackBlockedRequeststrue

当您的 Kestrel 服务器位于反向代理后面时,用于提取客户端 IP,如果您的代理使用不同的标头,则使用此选项进行设置。RealIpHeaderX-Real-IP

用于提取用于白名单的客户端 ID。如果此标头中存在客户端 ID,并且与 ClientWhitelist 中指定的值匹配,则不应用速率限制。ClientIdHeader

覆盖特定 IP 的一般规则appsettings.json

 "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 值和范围,如“192.168.0.0/24”、“fe80::/10”或“192.168.0.0-192.168.0.255”。

如果在 appsettings.json 配置文件中定义了静态速率策略,则需要在应用程序启动时为它们设定种子:

public static async Task Main(string[] args)
{
    IWebHost webHost = CreateWebHostBuilder(args).Build();

    using (var scope = webHost.Services.CreateScope())
    {
         // get the IpPolicyStore instance
         var ipPolicyStore = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();

         // seed IP data from appsettings
         await ipPolicyStore.SeedAsync();
    }

    await webHost.RunAsync();
}

定义速率限制规则

规则由终结点、周期和限制组成。

端点格式为 ,您可以使用 asterix 符号来定位任何 HTTP 谓词。{HTTP_Verb}:{PATH}

周期格式为 ,可以使用以下周期类型之一:。{INT}{PERIOD_TYPE}s, m, h, d

限制格式为 。{LONG}

示例

将所有终端节点的速率限制为每秒 2 次调用:

通配符:

{
 "Endpoint": "*",
 "Period": "1s",
 "Limit": 2
}

正则表达式:

{
 "Endpoint": ".+",
 "Period": "1s",
 "Limit": 2
}

如果在同一秒内从同一 IP 对 api/values 进行 3 次 GET 调用,则最后一次调用将被阻止。但是,如果在同一秒钟内您也调用了 PUT api/values,则请求将通过,因为它是不同的端点。启用终结点速率限制后,每个呼叫都会根据 .{HTTP_Verb}{PATH}

将任何 HTTP Verb 的调用限制为每 5 分钟 15 次调用:/api/values

通配符:

{
 "Endpoint": "*:/api/values",
 "Period": "15m",
 "Limit": 5
}

正则表达式:

{
 "Endpoint": ":/api/values",
 "Period": "15m",
 "Limit": 5
}

将 GET 呼叫的速率限制为每小时 5 次调用:/api/values

{
 "Endpoint": "get:/api/values",
 "Period": "1h",
 "Limit": 5
}

将 POST 或 PUT 呼叫的速率限制为每小时 5 次呼叫:/api/values

{
 "Endpoint": "((post)|(put)):/api/values",
 "Period": "1h",
 "Limit": 5
}

如果在一小时内从同一个 IP 对 api/values 进行 6 次 GET 调用,则最后一次调用将被阻止。但是,如果在同一小时内您也调用 GET api/values/1,则请求将通过,因为它是一个不同的终结点。

行为

当客户端进行 HTTP 调用时,执行以下操作:IpRateLimitMiddleware : RateLimitMiddleware<IpRateLimitProcessor>

  • 从请求对象中提取 IP、客户端 ID、HTTP 谓词和 URL,如果要实现自己的提取逻辑,可以覆盖该方法或实现自定义 IP/客户端解析器:IpRateLimitMiddleware.ResolveIdentity
public class CustomRateLimitConfiguration : RateLimitConfiguration
{
    protected override void RegisterResolvers()
    {
    	base.RegisterResolvers();

    	ClientResolvers.Add(new ClientQueryStringResolveContributor(HttpContextAccessor, ClientRateLimitOptions.ClientIdHeader));
    }
}
  • 在白名单中搜索 IP、客户端 ID 和 URL,如果匹配,则不执行任何操作。

  • 在 IP 规则中搜索匹配项,所有适用的规则都按时间段分组,每个时间段使用限制性最强的规则。

  • 在常规规则中搜索匹配项,如果匹配的常规规则具有 IP 规则中不存在的已定义时间段,则也会使用此常规规则。

  • 对于每个匹配的规则,速率限制计数器都会递增,如果计数器值大于规则限制,则请求将被阻止。

如果请求被阻止,则客户端会收到如下文本响应:

<span style="color:#1f2328"><span style="background-color:#ffffff"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
</code></span></span></span></span>

您可以通过更改这些选项来自定义响应,如果要实现自己的响应,可以覆盖 .标头值以秒为单位表示。HttpStatusCodeQuotaExceededMessageIpRateLimitMiddleware.ReturnQuotaExceededResponseRetry-After

如果请求未受到速率限制,则使用匹配规则中定义的最长周期来编写 X-Rate-Limit 标头,这些标头将注入响应中:

<span style="color:#1f2328"><span style="background-color:#ffffff"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>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
</code></span></span></span></span>

客户端可以像这样解析:X-Rate-Limit-Reset

<span style="color:#1f2328"><span style="background-color:#ffffff"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>DateTime resetDate = DateTime.ParseExact(resetHeader, "o", DateTimeFormatInfo.InvariantInfo);
</code></span></span></span></span>

可以通过将选项设置为 in appsettings.json 来禁用 X-Rate-Limit 和 Retry-After 标头。DisableRateLimitHeaderstrue

默认情况下,使用 记录被阻止的请求,如果要实现自己的日志记录,可以覆盖 . 当请求受到速率限制时,默认记录器会发出以下信息:Microsoft.Extensions.Logging.ILoggerIpRateLimitMiddleware.LogBlockedRequest

<span style="color:#1f2328"><span style="background-color:#ffffff"><span style="color:var(--fgColor-default, var(--color-fg-default))"><span style="background-color:var(--bgColor-muted, var(--color-canvas-subtle))"><code>info: AspNetCoreRateLimit.IpRateLimitMiddleware[0]
      Request get:/api/values from IP 84.247.85.224 has been blocked, quota 2/1m exceeded by 3. Blocked by rule *:/api/value, TraceIdentifier 0HKTLISQQVV9D.
</code></span></span></span></span>

在运行时更新速率限制

在应用程序启动时,定义的 IP 速率限制规则由或取决于您使用的缓存提供程序类型加载到缓存中。您可以访问控制器内部的 Ip 策略存储并修改 IP 规则,如下所示:appsettings.jsonMemoryCacheClientPolicyStoreDistributedCacheIpPolicyStore

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);

		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" }
			})
		});

		_ipPolicyStore.Set(_options.IpPolicyPrefix, pol);
	}
}

这样,您可以将 IP 速率限制存储在数据库中,并在每次应用程序启动后将其推送到缓存中。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A_nanda

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值