You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Express的后端API按用户限流方案咨询(JWT场景)

嘿,这个需求我之前在Express项目里实现过,刚好可以给你分享具体的思路和代码示例~

实现按用户维度的请求限流方案

1. 选择合适的限流库

推荐用express-rate-limit,这是Express生态里最常用的限流中间件,支持自定义限流标识和多种存储方式,灵活性拉满。如果是生产环境(多实例部署),还需要配合Redis做分布式限流,避免内存存储的局限性,这时候可以用rate-limit-redis作为存储适配器。

2. 从JWT中提取用户唯一标识

你提到用JWT鉴权,虽然你说“仅能获取用户的唯一token标识”,但后端在鉴权时其实可以解析JWT拿到用户的固定唯一ID(比如userId)——这比用token本身作为限流key更稳定,因为token过期刷新后会变化,而用户ID是永久固定的。如果确实只能拿到token字符串,用token作为key也可以,但建议尽量解析JWT获取用户ID。

先写一个从请求中提取用户标识的工具函数:

const jwt = require('jsonwebtoken');

function getUserIdentifier(req) {
  // 假设JWT放在Authorization头,格式为 "Bearer <token>"
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    // 未认证请求:可以用IP作为标识限流,或者直接拒绝,根据你的业务需求
    return req.ip;
  }

  const token = authHeader.split(' ')[1];
  try {
    // 解析JWT,替换成你的JWT密钥
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    // 返回用户唯一ID(优先选择这个,稳定性更高)
    return decoded.userId;
    // 如果确实只能用token作为标识,就返回token:
    // return token;
  } catch (err) {
    // 无效token:同样用IP处理
    return req.ip;
  }
}

3. 配置限流中间件

接下来配置限流规则,结合Redis存储(生产环境必备):

首先安装依赖:

npm install express-rate-limit rate-limit-redis ioredis jsonwebtoken

然后编写限流中间件:

const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const Redis = require('ioredis');

// 初始化Redis客户端(根据你的Redis配置调整)
const redisClient = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: process.env.REDIS_PORT || 6379,
  // 如果Redis有密码,添加这行:
  // password: process.env.REDIS_PASSWORD,
});

// 按用户维度限流的中间件
const userRateLimiter = rateLimit({
  // 生成唯一限流key:用我们刚才写的用户标识
  keyGenerator: (req) => getUserIdentifier(req),
  // 时间窗口:1分钟(单位毫秒)
  windowMs: 60 * 1000,
  // 每个窗口内的最大请求数:10次
  max: 10,
  // 超过限制后的响应内容(符合你的API格式)
  message: {
    code: 429,
    message: '请求过于频繁,请1分钟后再尝试'
  },
  // 生产环境用Redis存储,避免多实例限流不一致的问题
  store: new RedisStore({
    sendCommand: (...args) => redisClient.call(...args),
  }),
  // 是否跳过成功响应的计数?默认false,所有请求都计入
  // skipSuccessfulRequests: false,
});

4. 应用限流中间件

把这个中间件应用到需要限流的路由上,可以全局应用到所有API路由,也可以只给特定路由添加:

const express = require('express');
const app = express();

// 先应用你的JWT鉴权中间件(确保在限流中间件之前,这样才能正确提取用户标识)
// app.use(yourJwtAuthMiddleware);

// 全局应用到所有/api开头的路由
app.use('/api/', userRateLimiter);

// 示例路由
app.get('/api/user/info', (req, res) => {
  res.json({ code: 200, data: { username: 'JenuRudan' } });
});

app.listen(3000, () => {
  console.log('服务启动在3000端口');
});

关键注意事项

  • 生产环境必须用Redis存储:默认的内存存储在多实例部署或服务重启后会丢失计数,导致限流规则失效,Redis是分布式存储,能保证所有实例的计数一致。
  • 用户标识的选择:优先用用户ID而不是token,因为token会过期刷新,用用户ID可以保证限流计数的连续性。
  • 未认证请求处理:对于未携带JWT的请求,可以用IP作为限流key,避免恶意请求刷爆接口。

这样就能完美实现你要的“单个用户每分钟最多10次请求”的限流规则啦~

内容的提问来源于stack exchange,提问作者JenuRudan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:39:56