Skip to content

IP 限流

本页介绍 Ape‑Volo‑Admin 中 IP 限流的使用与实现,包括核心接口、注入与注册、配置说明、白名单/黑名单、分布式部署与最佳实践。

IP 限流实现一览

  • 基于 AspNetCoreRateLimit 中间件实现,支持按 IP + Endpoint 的限流策略。
  • 支持全局规则与端点级规则,支持白名单(IP/客户端/端点)。
  • 支持内存存储与 Redis 分布式存储,在多实例下共享计数器。
  • 支持自定义超限响应(状态码/内容类型/消息体)。

IP 限流注册

位置:Ape.Volo.Infrastructure/Extensions/IpRateLimitExtensions.cs

csharp
/// <summary>
/// IP限流扩展配置
/// </summary>
public static class IpRateLimitExtensions
{
    public static void AddIpStrategyRateLimitService(this IServiceCollection services)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));
        services.Configure<IpRateLimitOptions>(App.Configuration.GetSection("IpRateLimiting"));
        services.Configure<IpRateLimitPolicies>(App.Configuration.GetSection("IpRateLimitPolicies"));

        if (App.GetOptions<SystemOptions>().UseRedisCache)
        {
            var redisOptions = App.GetOptions<RedisOptions>();
            services.AddStackExchangeRedisCache(option =>
            {
                if (!string.IsNullOrWhiteSpace(redisOptions.Password))
                {
                    option.Configuration =
                        $"{redisOptions.Host}:{redisOptions.Port},password={redisOptions.Password},defaultDatabase={redisOptions.Index + 2}";
                }
                else
                {
                    option.Configuration = redisOptions.Host + ":" + redisOptions.Port;
                }

                option.InstanceName = "rateLimit:";
            });
            services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
            services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
        }
        else
        {
            services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
            services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
        }

        services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();
        services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
    }
}

说明:

  • SystemOptions.UseRedisCache = true 时,限流计数与策略存储使用 IDistributedCache(Redis),适合多实例部署;否则使用内存存储(单实例)。
  • AsyncKeyLockProcessingStrategy 用于同一 Key 的并发互斥,避免竞态条件导致计数不准。

中间件

位置:Ape.Volo.Infrastructure/Middleware/IpLimitMiddleware.cs

csharp
/// <summary>
/// IP限流策略中间件
/// </summary>
public static class IpLimitMiddleware
{
    private static readonly ILogger Logger = SerilogManager.GetLogger(typeof(IpLimitMiddleware));

    public static void UseIpLimitMiddleware(this IApplicationBuilder app)
    {
        if (app.IsNull())
            throw new ArgumentNullException(nameof(app));
        try
        {
            if (App.GetOptions<MiddlewareOptions>().IpLimit.Enabled)
            {
                app.UseIpRateLimiting();
            }
        }
        catch (Exception e)
        {
            Logger.Error($"Error occured limiting ip rate.\n{e.Message}");
            throw;
        }
    }
}

中间件启用顺序建议:

  • 放在 UseRouting() 之后、UseEndpoints() 之前,尽量靠前拦截以降低系统开销;与认证/授权的相对顺序可按业务选择。

🔧 IP 限流配置

配置文件设置

IpRateLimit.json 中配置限流相关参数(示例为有效 JSON):

json
{
  "IpRateLimiting": {
    "EnableEndpointRateLimiting": true,
    "StackBlockedRequests": false,
    "RealIpHeader": "X-Real-IP",
    "ClientIdHeader": "X-ClientId",
    "HttpStatusCode": 429,
    "IpWhitelist": ["127.0.0.1", "::1/10", "192.168.0.0/24"],
    "QuotaExceededResponse": {
      "Content": "{\"status\":429,\"message\":\"访问过于频繁,请稍后重试!\"}",
      "ContentType": "application/json",
      "StatusCode": 429
    },
    "EndpointWhitelist": ["get:/api/license", "*:/api/status"],
    "ClientWhitelist": ["dev-id-1", "dev-id-2"],
    "GeneralRules": [
      { "Endpoint": "get:/auth/captcha*", "Period": "5s", "Limit": 3 },
      { "Endpoint": "*/auth/*", "Period": "10s", "Limit": 5 },
      { "Endpoint": "*/api/*", "Period": "10s", "Limit": 8 }
    ]
  },
  "IpRateLimitPolicies": {
    "IpRules": [
      {
        "Ip": "127.0.0.1",
        "Rules": [{ "Endpoint": "*", "Period": "1s", "Limit": 5 }]
      }
    ]
  }
}

主要参数说明

参数说明示例/默认值
EnableEndpointRateLimiting是否启用端点级限流(按 HTTP 方法 + 路径匹配)true
StackBlockedRequests达到限制后是否继续累计阻塞请求的计数(一般保持 false)false
RealIpHeader反向代理透传的真实 IP 请求头X-Real-IP 或 X-Forwarded-For
ClientIdHeader客户端标识头(如需基于客户端的策略扩展)X-ClientId
HttpStatusCode超限返回状态码429
IpWhitelistIP 白名单(CIDR 支持)["127.0.0.1", "192.168.0.0/24"]
EndpointWhitelist端点白名单(支持通配符)["get:/api/license", "*:/api/status"]
ClientWhitelist客户端白名单["dev-id-1"]
QuotaExceededResponse超限自定义响应(内容、类型、状态码)application/json + 消息体
GeneralRules通用规则(所有未被特定策略覆盖的请求)见下方规则说明
IpRules针对某个 IP 的专属规则集合见下方规则说明

规则与匹配说明

  • Endpoint 格式:方法:路径,如 get:/api/user/*,支持 * 通配;*:/api/status 表示任意方法;* 表示所有端点。
  • Period 单位:s 秒、m 分、h 时、d 天。例如 5s10m1h
  • Limit:在一个 Period 窗口内允许的最大请求数(超过则返回 429)。
  • 匹配优先级:通常 IP 专属规则 > 端点白名单 > GeneralRules;具体以中间件实现为准。

真实 IP 与反向代理

  • 部署在 Nginx/Ingress/反向代理之后时,请确保代理正确设置 X-Real-IPX-Forwarded-For,并在应用中读取对应头(本示例配置为 RealIpHeader = X-Real-IP)。
  • 如需全面处理代理头,建议在 ASP.NET Core 中启用 ForwardedHeaders(示例):
csharp
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

分布式与存储选择

  • 单实例:使用内存存储(MemoryCacheIpPolicyStore/MemoryCacheRateLimitCounterStore)。
  • 多实例:推荐启用 Redis 并使用分布式存储,确保所有实例共享限流计数,避免各自为战。
  • 命名空间:本项目为分布式计数器设置了 InstanceName = "rateLimit:",并使用与业务缓存不同的 DB 索引(Index + 2),避免冲突。

响应与可观测性

  • 超限时默认返回 429;可通过 QuotaExceededResponse 自定义响应体与内容类型。
  • 建议记录被限流请求的日志(IP、端点、剩余配额、窗口起止时间),用于审计与调参。
  • 可在网关/代理层配合观测(如 Nginx Ingress 限流指标)进行端到端监控。

常见用法片段

按 IP 为登录与验证码分别限流:

json
{
  "GeneralRules": [
    { "Endpoint": "post:/auth/login", "Period": "60s", "Limit": 5 },
    { "Endpoint": "get:/auth/captcha*", "Period": "10s", "Limit": 3 }
  ]
}

给某个内网网段放开限制:

json
{
  "IpWhitelist": ["10.0.0.0/8", "192.168.0.0/16"]
}

为特定 IP 加严限制:

json
{
  "IpRateLimitPolicies": {
    "IpRules": [
      {
        "Ip": "203.0.113.42",
        "Rules": [{ "Endpoint": "*", "Period": "1m", "Limit": 30 }]
      }
    ]
  }
}

最佳实践与注意事项

  • 先从少量关键端点(如登录/验证码/搜索)启用,再按监控逐步覆盖,避免“一刀切”。
  • 规则要简洁:优先使用 GeneralRules + 少量特例,避免大量细碎规则导致维护困难。
  • 置于合适的中间件顺序,尽早拦截无效请求,降低系统消耗。
  • 多实例务必启用 Redis 分布式计数;并确认所有实例的时间同步(NTP)。
  • 与事务/缓存配合:被限流的写操作不会进入业务层,注意与上游重试策略的配合,避免流量放大;缓存可用于黑名单/频繁来源的快速判定。
  • 结合告警:对异常突增的 429 比例设置告警阈值,用于快速回溯与规则调整。

常见问题(FAQ)

  • 实际限流无效?
    • 检查 UseIpLimitMiddleware() 是否生效且顺序正确;
    • 确认 IpRateLimit.json 已加载且路径正确;
    • 在代理后部署时,确认 RealIpHeader 对应头已正确透传真实 IP。
  • 规则未命中?
    • 确认方法与路径大小写/通配符;get:/api/*GET:/api/* 的区分以配置/实现为准,建议统一小写;
    • 更具体的规则可能被白名单覆盖;逐条排查优先级。
  • 多实例计数不一致?
    • 确认已启用 Redis 分布式计数;
    • 检查 Redis 连接与实例名/DB 索引是否一致;
    • 确保实例间时间同步。

启用步骤小结

  1. 注册:services.AddIpStrategyRateLimitService()
  2. 中间件:app.UseIpLimitMiddleware()(放在合适顺序)
  3. 配置:准备有效的 IpRateLimit.json,按需设置白名单与规则
  4. 分布式:多实例部署时启用 Redis(SystemOptions.UseRedisCache = true)共享计数

版权所有 © 2021-2026 ApeVolo-Team