基于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
相关产品推荐
相关产品推荐

