5月27日 23:09
如何优化 JWT 的性能?
核心优化方向有三个:减小 Token 体积、加速签名验证、避免重复解析。
减小 Token 体积
JWT 每次请求都携带,体积直接影响带宽和解析耗时。
javascript// 只存必要字段,用短键名 const token = jwt.sign({ uid: "123", rol: "admin" }, SECRET); // 而非 { userId, userName, userRole, userPermissions }
- Payload 只放用户 ID 和角色,其他信息从缓存取
- 避免存数组、嵌套对象,每个字段都计入 Base64 编码后的体积
- 压缩(如 pako deflate)仅在 Payload > 1KB 时有意义,小 Payload 压缩后反而更大
加速签名验证
算法选择对性能影响最大:
| 算法 | 签名速度 | 验证速度 | Token 大小 | 适用场景 |
|---|---|---|---|---|
| HS256 | 最快 | 最快 | ~32B | 单体应用,密钥可安全共享 |
| RS256 | 慢 | 中 | ~256B | 微服务,公钥可公开分发 |
| ES256 | 中 | 快 | ~64B | 移动端/IoT,兼顾速度与体积 |
- HS256 比 RS256 快 5-10 倍,但密钥泄露风险更高
- ES256 是分布式场景的平衡选择:签名比 RS256 小 4 倍,验证更快
- 如果只用 HS256,可手写 HMAC 验证替代 jsonwebtoken 库,减少框架开销
避免重复解析
每次请求都 jwt.verify() 是浪费,可用缓存跳过:
javascript// 验证结果缓存(注意安全风险) const NodeCache = require("node-cache"); const cache = new NodeCache({ stdTTL: 300 }); // 缓存 5 分钟 function verifyCached(token) { const key = `tk:${crypto.createHash("sha256").update(token).digest("hex")}`; const hit = cache.get(key); if (hit) return hit; const decoded = jwt.verify(token, SECRET); cache.set(key, decoded); return decoded; }
缓存的安全代价:Token 被撤销(如用户登出)后,缓存期内仍可访问。解法:
- 缓存 TTL 设短(< Token 有效期的 1/10)
- 登出时主动清除对应用户的缓存 key
- 高安全场景不缓存,用算法优化替代
多实例部署时用 Redis 替代本地缓存,公钥(JWKS)也做缓存避免每次远程拉取。
异步验证
javascript// 同步验证阻塞事件循环 const decoded = jwt.verify(token, SECRET); // 阻塞 // 异步验证释放事件循环 const decoded = await new Promise((resolve, reject) => { jwt.verify(token, SECRET, (err, d) => err ? reject(err) : resolve(d)); });
高并发下同步 crypto 操作会阻塞 Node.js 事件循环,异步 + worker_threads 才是正解。
追问
Q: 缓存验证结果后,Token 撤销怎么办? 上面已提到,核心思路是短 TTL + 主动失效。也可以用 Token 版本号:用户登出时递增版本,缓存中版本不匹配则重新验证。
Q: JWT 无状态的意义在哪,加了缓存不就有状态了? 缓存是性能优化手段,不是架构依赖。缓存挂掉系统仍可用(降级为全量验证),这和 Session 必须依赖 Redis 有本质区别。
Q: 什么时候该用 JWT,什么时候该用 Session? 多服务/跨域/移动端选 JWT;单体内聚应用选 Session 更简单。JWT 的性能代价换来的是水平扩展能力,不是所有场景都需要。