news 2026/10/3 13:34:20

Node.js 最佳实践:为 JWT 身份认证实现黑名单(Blocklist)撤销机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Node.js 最佳实践:为 JWT 身份认证实现黑名单(Blocklist)撤销机制
  • 文档
  • 教程
  • 后端

【免费下载链接】nodebestpractices

✅ The Node.js best practices list (July 2026)

项目地址:https://gitcode.com/GitHub_Trending/no/nodebestpractices
点击查看免费下载

本篇技术指南聚焦 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。

撤销令牌的三个关键要素

要让黑名单机制真正可用,需要三个要素配合:

  1. 令牌唯一标识(jti):JWT 的 payload 中应携带jti(JWT ID)声明,作为该令牌的唯一 ID。黑名单以jti为键记录被吊销的令牌,从而精确定位到"这一张"令牌,而不是模糊地吊销整个用户。
  2. 过期时间(exp):JWT 应设置合理的exp声明。黑名单中记录的条目只需保留到对应令牌的exp时刻即可,过期后该令牌本已失效,可从黑名单中清理,控制存储规模。
  3. 每个请求校验:认证中间件在每一条受保护路由上,除了验签之外,还要将令牌的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,否则黑名单无法定位到具体令牌
stricttrue开启后,缺少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.timeout1000与存储交互的超时上限(毫秒),防止存储故障拖垮认证链路

blacklist.configure(...)必须在启动阶段、在挂载认证中间件之前完成调用,以确保全局配置生效。

为什么必须使用外部存储而非默认内存缓存

关联文档特别强调:不要使用express-jwt-blacklist默认的 in-memory 内存缓存设置,而应使用 Redis 这类外部存储来在多进程间共享撤销状态。

原因在于 Node.js 应用通常以多进程(cluster / PM2 多实例)甚至多节点方式部署:

  • 内存缓存只存在于单个进程内部,进程 A 拉黑的令牌,进程 B 完全感知不到;
  • 负载均衡把请求打到进程 B 时,被吊销的令牌依旧被放行,撤销机制形同虚设;
  • 进程重启后内存黑名单随之清空,撤销记录全部丢失。

而 Redis / Memcached 这类外部存储对所有进程可见、可共享、可持久化,保证了无论请求落在哪个进程上,黑名单校验结果一致。这也是示例中选择 Memcached(type: 'memcached')并配置keyPrefix、timeout等参数的用意所在。

工作机制:isRevoked与revoke的配合

整个流程可拆解为两个协作点:

  1. 每请求校验(isRevoked):app.use(jwt({ ... isRevoked: blacklist.isRevoked }))将express-jwt-blacklist提供的isRevoked回调接入express-jwt中间件。此后每一个请求在验签通过的同时,都会用令牌中的jti去黑名单存储中查询;一旦命中,该请求即被判定为使用已吊销令牌而拒绝。这正对应 README 6.11 条目中"在每个请求上验证"的要求。

  2. 主动吊销(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)

项目地址:https://gitcode.com/GitHub_Trending/no/nodebestpractices
点击查看免费下载
上一篇:如何用Open Mercato EAV自定义实体扩展数据模型:ce.ts实战指南
下一篇:Meta-Learning without Memorization:基于信息论元正则化的开源实现与姿态回归复现指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 13:29:57

【人脸识别】基于matlab GUI SVM和PCA人脸识别【含Matlab源码 369期】

💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞💞💞💞💞💞💥💥💥💥💥💥 ✅博主简介:热爱科研的Matlab仿真开发者,修心和技术同步精进; 🍎个人主页:海神之光 🏆代码获取方式: 海神之光Matlab王…

作者头像 李华