- 文档
- 教程
- 后端
【免费下载链接】nodebestpractices
✅ The Node.js best practices list (July 2026)
本篇技术指南聚焦 Node.js 项目中的 JWT(JSON Web Token)身份认证安全实践:由于 JWT 天然无状态、一经签发签名即长期有效,泄漏或失窃的令牌将无法被主动撤销。文中以express-jwt配合express-jwt-blacklist为例,完整讲解黑名单配置、外部存储选型(Redis / Memcached)与登出撤销流程,帮助你掌握在保留 JWT 便利性的同时补上"令牌吊销"这一关键安全短板的具体落地方案。
为什么 JWT 需要黑名单:无状态设计的双刃剑
无状态签名的安全隐患
JWT(JSON Web Token)在设计中是完全无状态的:一个有效的令牌一旦由签发方(issuer)签名,只要该签名与应用程序校验时所期望的签名一致,应用就会持续认可这个令牌的真实性与有效性。令牌本身不依赖任何服务端会话存储,校验只需本地验签即可完成。
这一特性带来了一个显著的安全隐患:一旦令牌泄漏,攻击者可以持续使用它访问系统,而应用侧没有任何机制将其撤销。只要泄漏令牌的签名仍然有效,应用就无法区分持有者是本人还是攻击者。
因此,在使用 JWT 身份认证时,应用程序应当维护一张已过期或被撤销令牌的黑名单(blacklist / blocklist),在需要吊销令牌的场景下(如用户登出、检测到恶意活动、令牌失窃)主动将对应令牌拉黑,以保护用户安全。
这一点在本仓库 README.chinese.md 的 6.11 条目中有精炼概括:
TL;DR:当使用 JSON Web Tokens(例如通过 Passport.js),默认情况下,没有任何机制可以从已发出的令牌中撤消访问权限。一旦发现一些恶意用户活动,只要它们持有有效的令牌,就无法阻止他们访问系统。通过实现一个不受信任令牌的黑名单,并在每个请求上验证,来减轻此问题。
否则:过期或错误的令牌可能被第三方恶意使用,以访问应用程序,并模拟令牌的所有者。
该条目同时带有 OWASP Threats 徽标,对应OWASP A9: Broken Authentication(失效的身份认证),说明令牌吊销属于身份认证环节的通用安全要求。英文原版条目见 README.md。
撤销令牌的三个关键要素
要让黑名单机制真正可用,需要三个要素配合:
- 令牌唯一标识(
jti):JWT 的 payload 中应携带jti(JWT ID)声明,作为该令牌的唯一 ID。黑名单以jti为键记录被吊销的令牌,从而精确定位到"这一张"令牌,而不是模糊地吊销整个用户。 - 过期时间(
exp):JWT 应设置合理的exp声明。黑名单中记录的条目只需保留到对应令牌的exp时刻即可,过期后该令牌本已失效,可从黑名单中清理,控制存储规模。 - 每个请求校验:认证中间件在每一条受保护路由上,除了验签之外,还要将令牌的
jti与黑名单比对,命中即拒绝放行。
express-jwt-blacklist实战:配置、登出撤销与每请求校验
下面是在 Node.js 项目中使用express-jwt搭配express-jwt-blacklist的完整示例(源自关联文档 expirejwt.md 并补齐配置注释):
const jwt = require('express-jwt'); const blacklist = require('express-jwt-blacklist'); blacklist.configure({ tokenId: 'jti', // 指定从 JWT payload 中读取哪个声明作为令牌唯一 ID strict: true, // 严格模式:当令牌缺少 jti 时直接拒绝,确保黑名单可用 store: { type: 'memcached', // 外部存储类型:memcached / redis host: '127.0.0.1', // 存储服务地址 port: 11211, // 存储服务端口 keyPrefix: 'mywebapp:', // 键前缀,隔离不同应用/环境的黑名单键 options: { timeout: 1000 // 存储读写超时(毫秒) } } }); // 认证中间件:每次请求验签,并调用 isRevoked 检查令牌是否已被拉黑 app.use(jwt({ secret: 'my-secret', isRevoked: blacklist.isRevoked })); // 登出端点:将当前用户令牌加入黑名单,完成主动吊销 app.get('/logout', (req, res) => { blacklist.revoke(req.user) res.sendStatus(200); });关键配置项逐项解读
| 配置项 | 取值示例 | 作用与注意事项 |
|---|---|---|
tokenId | 'jti' | 指定 JWT payload 中承载唯一 ID 的声明名。务必在签发令牌时写入jti,否则黑名单无法定位到具体令牌 |
strict | true | 开启后,缺少tokenId声明(即没有jti)的令牌会被视为无效而拒绝,从源头保证每个令牌都可被吊销 |
store.type | 'memcached'/'redis' | 外部存储类型。强烈不建议使用默认的 in-memory 内存缓存,原因见下文 |
store.host/store.port | '127.0.0.1'/11211 | 外部存储的连接地址与端口(Memcached 默认端口 11211,Redis 默认 6379) |
store.keyPrefix | 'mywebapp:' | 黑名单键的前缀,用于在共享存储中隔离不同应用或环境,避免键冲突 |
store.options.timeout | 1000 | 与存储交互的超时上限(毫秒),防止存储故障拖垮认证链路 |
blacklist.configure(...)必须在启动阶段、在挂载认证中间件之前完成调用,以确保全局配置生效。
为什么必须使用外部存储而非默认内存缓存
关联文档特别强调:不要使用express-jwt-blacklist默认的 in-memory 内存缓存设置,而应使用 Redis 这类外部存储来在多进程间共享撤销状态。
原因在于 Node.js 应用通常以多进程(cluster / PM2 多实例)甚至多节点方式部署:
- 内存缓存只存在于单个进程内部,进程 A 拉黑的令牌,进程 B 完全感知不到;
- 负载均衡把请求打到进程 B 时,被吊销的令牌依旧被放行,撤销机制形同虚设;
- 进程重启后内存黑名单随之清空,撤销记录全部丢失。
而 Redis / Memcached 这类外部存储对所有进程可见、可共享、可持久化,保证了无论请求落在哪个进程上,黑名单校验结果一致。这也是示例中选择 Memcached(type: 'memcached')并配置keyPrefix、timeout等参数的用意所在。
工作机制:isRevoked与revoke的配合
整个流程可拆解为两个协作点:
每请求校验(
isRevoked):app.use(jwt({ ... isRevoked: blacklist.isRevoked }))将express-jwt-blacklist提供的isRevoked回调接入express-jwt中间件。此后每一个请求在验签通过的同时,都会用令牌中的jti去黑名单存储中查询;一旦命中,该请求即被判定为使用已吊销令牌而拒绝。这正对应 README 6.11 条目中"在每个请求上验证"的要求。主动吊销(
revoke):在/logout这样的登出端点中调用blacklist.revoke(req.user),把当前请求对应的用户令牌 ID 写入黑名单存储,随后返回200。用户登出后,即便其旧令牌仍在exp有效期之内,也无法再通过isRevoked校验。
引入黑名单的代价与配套实践
失去无状态性:可接受的权衡
必须正视的事实是:引入黑名单意味着 JWT失去了一部分无状态特性——服务端现在需要维护一份"已吊销令牌"的共享状态。关联文档引用了 Marc Busqué 的观点:
……在 JWT 之上添加一个吊销层(revocation layer),即使它意味着失去其无状态性质。
这是一种有意识的权衡:相比"完全无状态但泄漏令牌无法撤销"的风险,用一份受控的外部存储换取"令牌可主动吊销"的安全能力,在大多数生产场景下是更优的选择。
控制黑名单的规模与生命周期
黑名单本质上是"令牌 ID 集合",其规模与令牌签发量、撤销频率成正比。为控制存储增长,建议:
- 为所有令牌设置合理且较短的
exp,过期令牌本就失效,黑名单条目随之失去意义; - 为黑名单条目设置与令牌
exp一致的 TTL,让存储自动清理过期条目,避免无限膨胀; - 仅在确实需要吊销时(登出、失窃、封禁、检测到异常活动)才写入黑名单,而不是默认对所有令牌全部拉黑。
与仓库中其他安全实践的衔接
本仓库的 commonsecuritybestpractices.md 将 JWT 吊销归类为通用安全准则之一;配套的安全章节还提供了多层面的纵深防御思路:
- sessions.md:会话中间件安全设置(如
httpOnly、securecookie),适用于改用/混用服务端会话的场景; - login-rate-limit.md:限制登录尝试次数,配合 README 6.12 条目防范暴力破解,降低令牌被暴力获取的风险;
- expirejwt.md:本文所依据的关联文档原文,含完整示例与引用出处。
这些实践与 JWT 黑名单共同构成"签发—校验—吊销"闭环,是生产级 Node.js 应用身份安全的重要组成部分。
小结
JWT 的无状态签名特性决定了"泄漏即失控",而黑名单机制正是弥补这一缺陷的工程化答案。通过express-jwt-blacklist的configure配置(tokenId: 'jti'、strict: true、外部 Memcached/Redis 存储),配合isRevoked每请求校验与/logout中的revoke主动吊销,Node.js 应用可以在保留 JWT 便利性的同时,获得与"签发能力"对等的"撤销能力"。唯一要牢记的前提是:务必使用外部共享存储承载黑名单,否则多进程部署下撤销将形同虚设。
- 文档
- 教程
- 后端
【免费下载链接】nodebestpractices
✅ The Node.js best practices list (July 2026)
相关推荐
Node.js 安全实践:为 JWT 实现令牌黑名单(Blocklist)撤销机制
Node.js 安全实践:为 JWT 实现令牌黑名单(Blocklist)撤销机制 导读 JWT(JSON Web Token)身份认证的“完全无状态”特性既是
文档教程后端nodebestpractices 安全实践:为 Node.js 应用实现 JWT 黑名单(Blocklist),让已泄露的令牌真正可撤销
nodebestpractices 安全实践:为 Node.js 应用实现 JWT 黑名单(Blocklist),让已泄露的令牌真正可撤销 JWT(JSON W
文档教程后端Node.js 实践指南:为 JWT 认证增加令牌黑名单(Token Revocation)机制
Node.js 实践指南:为 JWT 认证增加令牌黑名单(Token Revocation)机制 本文基于 nodebestpractices https://
文档教程后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考