在 ASP.NET Core 中实现限流(Rate Limiting):保护服务免受滥用与攻击

发布于:2025-03-27 ⋅ 阅读:(19) ⋅ 点赞:(0)

引言

在现代 Web 开发中,API 的高并发访问可能导致资源耗尽或服务崩溃,尤其在面对恶意攻击(如 DoS)时,限流(Rate Limiting)是保护服务的核心策略。ASP.NET Core 提供了多种限流方案,既支持内置中间件(.NET 7+),也可通过第三方库实现更复杂的场景。本文将详细介绍两种主流实现方案,并分析不同限流策略的适用场景。


一、方案一:ASP.NET Core 内置限流(.NET 7+)

从 .NET 7 开始,ASP.NET Core 原生集成了限流中间件 Microsoft.AspNetCore.RateLimiting,支持固定窗口、滑动窗口和令牌桶等策略。

1. 安装与配置

首先安装 NuGet 包:

dotnet add package Microsoft.AspNetCore.RateLimiting

Program.cs 中配置限流服务:

using Microsoft.AspNetCore.RateLimiting;
using System.Threading.RateLimiting;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRateLimiter(options => 
{
    // 示例:固定窗口策略(每分钟允许100次请求)
    options.AddFixedWindowLimiter("FixedPolicy", opt => 
    {
        opt.PermitLimit = 100;       // 时间窗口内允许的请求数
        opt.Window = TimeSpan.FromMinutes(1);
        opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; // 处理队列顺序
        opt.QueueLimit = 10;         // 允许排队的请求数
    });

    // 其他策略(滑动窗口、令牌桶等)
    options.AddSlidingWindowLimiter("SlidingPolicy", ...);
    options.AddTokenBucketLimiter("TokenPolicy", ...);

    // 全局拒绝响应配置
    options.OnRejected = (context, _) => 
    {
        context.HttpContext.Response.StatusCode = 429;
        return ValueTask.CompletedTask;
    };
});

var app = builder.Build();
app.UseRateLimiter(); // 启用限流中间件(需在 UseRouting 之后)
2. 应用限流策略
  • 全局限流:默认对所有端点生效。
  • 端点级限流:通过 RequireRateLimiting 指定策略:
app.MapGet("/api/data", () => "Data").RequireRateLimiting("FixedPolicy");
3. 策略类型与适用场景
策略 原理 适用场景
固定窗口 在固定时间窗口(如1分钟)内限制请求总数。 简单高频请求(如秒杀)
滑动窗口 将窗口分为多个段,随时间滑动更新计数,平滑流量。 需要更均匀限制的场景
令牌桶 按固定速率生成令牌,请求需消耗令牌,允许突发流量。 突发流量(如批量操作)
并发控制 限制同时处理的请求数,超出则排队或拒绝。 资源密集型操作(如文件上传)

二、方案二:第三方库 AspNetCoreRateLimit

若需要更细粒度的控制(如基于 IP、客户端 ID 或自定义规则),推荐使用 AspNetCoreRateLimit。它支持从配置文件动态加载策略,并集成分布式缓存。

1. 安装与配置

安装 NuGet 包:

dotnet add package AspNetCoreRateLimit

配置服务与策略:

var builder = WebApplication.CreateBuilder(args);

// 加载配置文件中的策略
builder.Services.Configure<IpRateLimitOptions>(builder.Configuration.GetSection("IpRateLimiting"));
builder.Services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
builder.Services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
builder.Services.AddMemoryCache();

var app = builder.Build();
app.UseIpRateLimiting(); // 启用IP限流中间件
2. 配置文件示例(appsettings.json)
{
  "IpRateLimiting": {
    "EnableEndpointRateLimiting": true,
    "GeneralRules": [
      {
        "Endpoint": "*",          // 所有端点
        "Period": "1m",           // 时间窗口
        "Limit": 100              // 允许的请求数
      },
      {
        "Endpoint": "POST:/api/orders", 
        "Period": "10s", 
        "Limit": 5                // 针对特定接口严格限流
      }
    ]
  }
}
3. 高级功能
  • 基于客户端标识:通过请求头(如 X-ClientId)区分客户端。
  • 动态策略调整:运行时更新策略,无需重启服务。
  • 分布式缓存支持:结合 Redis 实现多节点一致性限流。

三、关键注意事项

  1. 中间件顺序
    确保 UseRateLimiter()UseRouting() 之后调用,否则策略可能不生效。

  2. 分布式环境
    若服务部署在多节点,需使用分布式缓存(如 Redis)替代内存存储,避免单节点计数不准确:

    builder.Services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
    
  3. 测试与监控

    • 使用 Postman 或 curl 发送高并发请求验证限流。
    • 记录被拒绝的请求日志,分析策略合理性:
    options.OnRejected = (context, _) => 
    {
        logger.LogWarning($"请求被拒绝:{context.HttpContext.Request.Path}");
        return ValueTask.CompletedTask;
    };
    
  4. 灵活选择策略

    • 固定窗口:简单但可能允许突发流量。
    • 滑动窗口:平滑流量,适合均衡限制。
    • 令牌桶:允许突发,适合秒杀类场景。

四、总结

ASP.NET Core 提供了从内置中间件到第三方库的多种限流方案:

  • 内置方案:适合简单场景,无需额外依赖,支持主流限流算法。
  • AspNetCoreRateLimit:适合复杂需求,支持动态配置、客户端级限流和分布式部署。

通过合理配置限流策略,可以有效防止服务过载,提升系统稳定性。建议结合监控日志持续优化阈值,确保用户体验与系统安全的平衡。