白名单与限流独立性说明文档

📊 核心问题

用户需求:白名单和限流必须是完全独立的两个功能,不是耦合的。

  • 白名单:访问控制(IP 不在白名单 → 403 Forbidden)
  • 限流:速率控制(请求超限 → 429 Too Many Requests)
  • 独立性:即使 IP 在白名单内,也要受到限流限制

⚡ 快速参考:配置场景

场景对比表

配置情况处理结果推荐度
只配置限流所有 IP 可访问 + 限流⭐⭐⭐ 适合公开 API
只配置白名单白名单 IP 无限制访问❌ 不推荐
白名单 + 限流白名单验证 → 限流检查⭐⭐⭐ 最推荐
全局白名单所有路由通用 + 各自限流⭐⭐⭐ 推荐

详细说明: 配置场景详解

关键要点

  1. 未配置白名单 = 允许所有 IP(不是拒绝所有)
  2. 白名单 IP 也会被限流(独立版本)
  3. 全局白名单优先级更高(但仍需限流检查)
  4. 推荐配置:白名单 + 限流(双重保护)

❌ 错误实现(耦合版本)

问题代码

之前的 express-ip-whitelist-advanced.js 等文件中:

function createRouteLimiter(route, options = {}) {
  return new RateLimiter({
    windowMs: options.windowMs || 60 * 1000,
    max: options.max || 50,
    skip: (req) => {
      const clientIP = req.ip || req.socket?.remoteAddress;
      
      // ❌ 问题:白名单 IP 跳过了限流
      if (ipConfig.isGlobalWhitelisted(clientIP)) {
        return true; // 跳过限流
      }
      
      if (ipConfig.isRouteWhitelisted(route, clientIP)) {
        return true; // 跳过限流
      }
      
      return false;
    },
  });
}

问题分析

问题描述后果
耦合白名单检查混在限流器的 skip白名单和限流不独立
跳过限流白名单 IP 使用 skip: true 跳过限流白名单 IP 不受限流控制
功能混淆一个配置同时控制两个功能无法独立配置白名单和限流

实际效果

白名单 IP:
  → skip: true → 完全跳过限流 ❌
  → 无限制访问 ❌

非白名单 IP:
  → skip: false → 应用限流 ✅
  → 受到限流限制 ✅

结论:白名单 IP 不受限流控制,两者是耦合的 ❌


✅ 正确实现(独立版本)

核心原则

请求 → [白名单中间件] → [限流中间件] → [业务处理]
         ↓ 不在白名单      ↓ 超限
       403 Forbidden    429 Too Many

两个独立的中间件

  1. 白名单中间件:只负责验证 IP
  2. 限流中间件:只负责速率控制

实现代码

1. 白名单中间件(独立)

/**
 * IP 白名单验证中间件
 * - 只负责验证 IP 是否在白名单
 * - 不在白名单 → 403 Forbidden
 * - 在白名单 → 继续执行(包括限流检查)
 */
function ipWhitelistMiddleware(route) {
  return (req, res, next) => {
    const clientIP = req.ip || req.socket?.remoteAddress;

    // 检查全局白名单
    if (ipConfig.isGlobalWhitelisted(clientIP)) {
      return next(); // ✅ 通过验证,继续到限流
    }

    // 检查路由白名单
    if (ipConfig.isRouteWhitelisted(route, clientIP)) {
      return next(); // ✅ 通过验证,继续到限流
    }

    // ❌ 不在白名单,拒绝访问
    res.status(403).json({
      error: '访问被拒绝',
      message: '只有授权的 IP 地址可以访问此资源',
      ip: clientIP,
    });
  };
}

2. 限流中间件(独立)

/**
 * 限流中间件
 * - 只负责速率限制
 * - 不检查白名单(白名单由独立中间件处理)
 * - 超过限额 → 429 Too Many Requests
 */
function createRateLimiter(options = {}) {
  const limiter = new RateLimiter({
    windowMs: options.windowMs || 60 * 1000,
    max: options.max || 100,
    // ⚠️ 注意:不使用 skip,所有请求都要限流
  });

  return limiter.middleware();
}

3. 组合使用

// 管理后台:白名单 + 限流(完全独立)
const adminWhitelist = ipWhitelistMiddleware('/api/admin');
const adminLimiter = createRateLimiter({ max: 200 });

app.get('/api/admin/users',
  adminWhitelist,  // 第一层:白名单验证(403)
  adminLimiter,    // 第二层:限流控制(429)
  (req, res) => {
    // 业务处理
  }
);

实际效果

白名单 IP(200次/分钟限流):
  请求 1-200:
    → 白名单验证 ✅ → 限流检查 ✅ → 200 OK
  
  请求 201+:
    → 白名单验证 ✅ → 限流检查 ❌ → 429 Too Many Requests

非白名单 IP:
  任何请求:
    → 白名单验证 ❌ → 403 Forbidden(不会到达限流检查)

结论:白名单 IP 也受限流控制,两者完全独立 ✅


📋 对比表格

特性耦合版本 ❌独立版本 ✅
白名单 IP 是否限流❌ 不限流(skip: true)✅ 限流(独立中间件)
执行顺序限流器内部检查白名单白名单 → 限流(顺序明确)
配置独立性❌ 混在一起✅ 完全独立
功能清晰度❌ 混淆✅ 清晰
测试难度❌ 难测试✅ 易测试

🧪 测试验证

测试场景 1: 白名单 IP 被限流

# 快速请求测试接口 6 次(限流 5 次/分钟)
for i in {1..6}; do
  curl http://localhost:3500/api/test/independence
  echo ""
done

预期结果(独立版本):

请求 1: 200 OK - {"remaining": 4}
请求 2: 200 OK - {"remaining": 3}
请求 3: 200 OK - {"remaining": 2}
请求 4: 200 OK - {"remaining": 1}
请求 5: 200 OK - {"remaining": 0}
请求 6: 429 Too Many Requests ✅

错误结果(耦合版本):

请求 1-6: 全部 200 OK(无限制)❌

测试场景 2: 非白名单 IP

# 使用非白名单 IP 访问
curl http://localhost:3500/api/admin/users

预期结果(两个版本一致):

403 Forbidden
{
  "error": "访问被拒绝",
  "message": "只有授权的 IP 地址可以访问此资源"
}

📁 文件对比

文件类型文件名特点推荐
耦合版本express-ip-whitelist-advanced.js白名单跳过限流
耦合版本koa-ip-whitelist-advanced.js白名单跳过限流
独立版本express-ip-whitelist-independent.js白名单 + 限流独立
独立版本koa-ip-whitelist-independent.js白名单 + 限流独立

🎯 使用建议

场景 1: 公开 API

需求:所有人可以访问,但有限流

配置

// 不需要白名单中间件
app.get('/api/public/data',
  createRateLimiter({ max: 100 }),  // 只需要限流
  handler
);

场景 2: 管理后台

需求:只允许办公室 IP,且有限流

配置

// 需要白名单 + 限流(独立)
app.get('/api/admin/users',
  ipWhitelistMiddleware('/api/admin'),  // 白名单验证
  createRateLimiter({ max: 200 }),      // 限流控制
  handler
);

场景 3: 内部 API

需求:只允许内网 IP 段,高限流

配置

// IP 段白名单 + 高限流
app.get('/api/internal/stats',
  ipWhitelistMiddleware('/api/internal'),  // IP 段验证
  createRateLimiter({ max: 5000 }),        // 高限流
  handler
);

场景 4: 不同操作不同限流

需求:同一白名单,不同操作不同限流

配置

// 敏感操作:低限流
app.post('/api/secure/delete',
  ipWhitelistMiddleware('/api/secure'),  // 共享白名单
  createRateLimiter({ max: 10 }),        // 低限流
  handler
);

// 查询操作:高限流
app.get('/api/secure/query',
  ipWhitelistMiddleware('/api/secure'),  // 共享白名单
  createRateLimiter({ max: 1000 }),      // 高限流
  handler
);

🚀 快速开始

Express 独立版本

cd E:\MySelf\flex-rate-limit

# 启动服务(端口 3500)
GLOBAL_IP_WHITELIST=127.0.0.1 \
ADMIN_IP_WHITELIST=192.168.1.10,192.168.1.11 \
node examples/express-ip-whitelist-independent.js

# 测试独立性
for i in {1..6}; do
  curl http://localhost:3500/api/test/independence
  echo ""
done

Koa 独立版本

# 启动服务(端口 3501)
PORT=3501 GLOBAL_IP_WHITELIST=127.0.0.1 \
node examples/koa-ip-whitelist-independent.js

# 测试
for i in {1..6}; do
  curl http://localhost:3501/api/test/independence
  echo ""
done

📚 相关文档

  • Express 独立版本: examples/express-ip-whitelist-independent.js
  • Koa 独立版本: examples/koa-ip-whitelist-independent.js
  • 耦合版本(参考): examples/express-ip-whitelist-advanced.js

✅ 总结

核心区别

方面耦合版本独立版本
白名单 IP 限流❌ 不限流✅ 限流
实现方式单个中间件(skip)两个独立中间件
配置灵活性❌ 低✅ 高
功能清晰度❌ 混淆✅ 清晰
推荐使用❌ 不推荐✅ 推荐

关键点

  1. 白名单中间件:只负责验证 IP(403)
  2. 限流中间件:只负责速率控制(429)
  3. 完全独立:白名单 IP 也会被限流
  4. 顺序清晰:白名单 → 限流 → 业务

推荐使用

  • ✅ 使用 express-ip-whitelist-independent.js
  • ✅ 使用 koa-ip-whitelist-independent.js
  • ❌ 避免使用耦合版本

文档创建时间: 2026-02-05
版本: v1.0
状态: ✅ 完成