ChatGPT解决这个技术问题 Extra ChatGPT

如何撤销 JWT 令牌?

我正在使用 Spring Security OAuth2 和 JWT 令牌。我的问题是:如何撤销 JWT 令牌?

正如这里提到的 http://projects.spring.io/spring-security-oauth/docs/oauth2.html,撤销是通过刷新令牌完成的。但这似乎不起作用。

你不能,除非你在授权服务器上建立一些令牌记录。使用令牌的资源 API 还必须检查令牌是否被撤销。
你有没有发现什么?另外,如果我要建立令牌记录,我认为 Oauth 不是无国籍的??
+1 对 Shaun 的评论,并补充说它通常会破坏使用 JWT(或按值)令牌这样做的意义。
肖恩的评论是错误的,或者至少不完全正确。您必须区分访问令牌和刷新令牌。虽然使访问令牌无效没有意义,但您可以使用刷新令牌来做到这一点。在您的访问令牌到期时间为例如 15 并且刷新令牌可能为一周的情况下,您可以轻松地看到通过使刷新令牌无效可以实现的目标。这样您就不必向资源服务器传播任何内容,也不会失去无状态性。
...这当然假设您只刷新身份验证服务器上的访问令牌。

C
Community

一般来说,最简单的答案是说您不能撤销 JWT 令牌,但这根本不是真的。诚实的答案是,支持 JWT 撤销的成本足够大,以至于在大多数情况下都不值得,或者明确地重新考虑 JWT 的替代方案。

话虽如此,在某些情况下,您可能需要 JWT 和立即撤销令牌,所以让我们来看看它需要什么,但首先我们将介绍一些概念。

JWT (Learn JSON Web Tokens) 只是指定了一种令牌格式,这个吊销问题也适用于通常称为自包含或按值令牌的任何格式。我喜欢后一种术语,因为它与引用标记形成了很好的对比。

按值令牌 - 相关信息,包括令牌生命周期,包含在令牌本身中,并且可以验证信息是否源自可信来源(救援的数字签名) 引用令牌 - 相关信息保存在服务器端然后使用令牌值作为键获得的存储;作为服务器端存储,相关信息是隐式信任的

在 JWT Big Bang 之前,我们已经在身份验证系统中处理了令牌;应用程序通常会在用户登录时创建一个会话标识符,然后使用该标识符,这样用户就不必每次都重复登录过程。这些会话标识符被用作服务器端存储的关键索引,如果这听起来与您最近阅读的内容相似,那么您是对的,这确实归类为按引用标记。

使用相同的类比,理解引用令牌的撤销是微不足道的;我们只是删除映射到该密钥的服务器端存储,下次提供该密钥时它将无效。

对于按价值代币,我们只需要执行相反的操作。当您请求撤销令牌时,您存储的内容允许您唯一标识该令牌,以便下次收到它时,您可以另外检查它是否已被撤销。如果您已经在考虑这样的事情不会扩展,请记住,您只需要存储数据直到令牌过期,并且在大多数情况下,您可能只存储令牌的哈希值,因此它总是是已知大小的东西。

最后一点,并将其集中在 OAuth 2.0 上,按值访问令牌的撤销目前尚未标准化。尽管如此,OAuth 2.0 令牌撤销明确指出,只要授权服务器和资源服务器都同意自定义处理方式,它仍然可以实现:

在前一种情况下(自包含令牌),当需要立即撤销访问令牌时,可以使用授权服务器和资源服务器之间的一些(当前非标准化)后端交互。

如果您同时控制授权服务器和资源服务器,这很容易实现。另一方面,如果您将授权服务器角色委托给像 Auth0 这样的云提供商或像 Spring OAuth 2.0 这样的第三方组件,您很可能需要以不同的方式处理事情,因为您可能只会获得已经标准化的内容。

一个有趣的参考

本文解释了另一种方法:Blacklist JWT 它包含一些有趣的实践和模式,然后是 RFC7523


不是为每个未过期的令牌存储一个哈希(虽然可以管理,但可能有很多哈希),您是否可以只为每个撤销的令牌存储一个哈希,并检查它是否不存在而不是存在?
文本不是很清楚,但是是的,我说的是只为撤销的按值令牌存储一些东西,并且也只在它们没有达到正常到期之前存储一些东西。每个令牌都会像往常一样被验证,然后会进行额外的检查以查看它是否未被撤销。
+2/2 (= 100) 用于引入术语“按值”和“按引用”:)
“按值”令牌确实与会话概念相反,即在“删除”会话时存储对 (jti, exp) 并分发所有这些对,而服务器可以独立于任何集中式存储在 exp 上继续删除它们。可以很好地扩展,但在这些撤销 jti id 方面仍然有状态的“反会话”。
H
Huanghq

JWT 不能被撤销。

但这里有一个替代解决方案,称为 JWT old for new exchange schema。

因为我们不能在过期时间之前使颁发的令牌失效,所以我们总是使用短时间令牌,例如 30 分钟。当令牌过期时,我们用旧令牌交换一个新令牌。关键是一个旧代币只能换一个新代币。

在中心认证服务器中,我们维护一个这样的表:

table auth_tokens(
    user_id,
    jwt_hash,
    expire
)

JWT 字符串中包含的 user_id。 jwt_hash 是整个 JWT 字符串的哈希值,如 SHA256。过期字段是可选的。

以下是工作流程:

用户使用用户名和密码请求登录API,认证服务器颁发一个令牌,并注册令牌(在表中添加一行。)当令牌过期时,用户使用旧令牌请求交换API。首先,auth 服务器正常验证旧令牌,除了过期检查,然后创建令牌哈希值,然后通过用户 id 查找上表:如果找到的记录与 user_id 和 jwt_hash 匹配,则发出新令牌并更新表。如果找到记录,但 user_id 和 jwt_hash 不匹配,则表示之前有人使用过该令牌交换了新令牌。令牌被黑客入侵,通过 user_id 删除记录并响应警报信息。如果没有找到记录,用户需要重新登录或只输入密码。当使用更改密码或登出时,按用户ID删除记录。

要持续使用token,合法用户和黑客都需要不断交换新的token,但只有一个可以成功,当一个失败时,都需要在下次交换时间重新登录。

所以如果黑客拿到了token,可以使用很短的时间,但是如果是合法用户下次换新的,就不能换新的了,因为token的有效期很短。这种方式更安全。

如果没有黑客,普通用户也需要定期交换新令牌,比如每 30 分钟一次,这就像自动登录一样。额外的负载并不高,我们可以为我们的应用程序调整过期时间。

来源:http://www.jianshu.com/p/b11accc40ba7


此解决方案的主要缺点是同时只能有一个用户登录。
如果用户停止使用系统并且黑客换取新的令牌怎么办?听起来黑客可以继续使用和交换令牌,直到用户再次登录。呸。
这是一个不应该被赞成的答案。如前所述,提议的答案将黑名单与刷新混合在一起,这意味着令牌现在既是“有状态的”又是“单例的”(仅限单次登录),这两者都完全违反了 JWT 原则。然而,提议的解决方案仍然存在依赖到期(留下一个主要弱点)的问题,同时引入了一个新的解决方案(单次登录)。因此,这比拥有简单的 auth jwt + 刷新令牌和黑名单机制还要糟糕。老实说:它也不是最简单的实现(可能存在错误)。
I
Ian Storm Taylor

这并不能完全回答您关于 Spring 框架的问题,但这里有一篇文章讨论了为什么如果您需要撤销 JWT 的能力,您可能不想首先使用 JWT,而是使用常规,不透明的承载令牌。

https://www.dinochiesa.net/?p=1388


为什么不使用带有 cron 的缓存或 rdbm 并定期删除每个条目或类似的东西而不是等待?
k
kstra

撤销 JWT 的一种方法是利用分布式事件系统,当刷新令牌被撤销时通知服务。当刷新令牌被撤销并且其他后端/服务侦听该事件时,身份提供者会广播一个事件。当接收到事件时,后端/服务会更新本地缓存,该缓存维护一组刷新令牌已被撤销的用户。

每当验证 JWT 以确定是否应撤销 JWT 时,都会检查此缓存。这一切都基于 JWT 的持续时间和各个 JWT 的到期时间。

这篇文章 Revoking JWTs 说明了这个概念,并在 Github 上提供了一个示例应用程序。


该“撤销 JWT”链接现在返回 404。
@DenverCoder9 也许令牌已成功撤销
d
dz902

对于 Google 员工:

如果您实施纯无状态身份验证,则无法撤销令牌,因为令牌本身是唯一的事实来源

如果您在服务器上保存已撤销的令牌 ID 列表并根据列表检查每个请求,那么它本质上是有状态身份验证的变体

像 Cognito 这样的 OAuth2 提供者提供了一种“退出”用户的方法,但是,它只真正撤销了刷新令牌,该令牌通常是长期存在的,并且可以多次使用以生成新的访问令牌,因此必须被撤销;现有的访问令牌在过期之前仍然有效


是否有任何替代 JWT 可以支持令牌撤销但可用于实现纯无状态身份验证的方法?
c
codepushr

存储 JWT 令牌并将其引用给数据库中的用户怎么样?通过在执行 JWT 比较后使用额外的数据库连接扩展后端应用程序中的警卫/安全系统,您实际上可以通过从数据库中删除或软删除它来“撤销”它。


您必须将令牌视为密码,因此与其存储令牌本身,不如将其存储为 la bcrypt、scrypt 等的哈希值。但是,您将引入一个额外的令牌验证步骤,该步骤需要集中化,否定JWT 的优势之一,那么你真的想要 JWT 吗?实际上,链接到以下文章的人更详细地表达了相同的观点:dinochiesa.net/?p=1388
b
bigkahunaburger

一般来说,关于引用代币与价值代币的答案已经确定了。对于那些将来偶然发现这个空间的人。

如何在 RS 端实现吊销:TL;DR:获取对所有正在验证令牌的后端服务实例可见的缓存或数据库。当一个新的令牌到达撤销时,如果它是一个有效的,(即根据你的 jwt 验证算法进行验证),获取 exp 和 jti 声明,并将 jti 保存到缓存,直到达到 exp。然后一旦 unixNow 变为 > exp,缓存中的 jti 就会过期。

然后在其他端点上授权时,您每次检查给定的 jti 是否与此缓存中的某些内容匹配,如果是,则错误 403 表示令牌已撤销。一旦过期,您的验证算法中就会出现常规的 Token Expired 错误。

PS 通过仅将 jti 保存在缓存中,您可以使这些数据对任何人都无用,因为它只是一个唯一的令牌标识符。


我想评论 wrt “关于其他端点的授权,您每次检查给定的 jti 是否与此缓存中的某些内容匹配”,它在分发撤销列表时增加了撤销令牌的一致性同步延迟。 JWT 并不神奇,它们只是颠倒了会话的概念,成为它的概念“补充”。尽管如此,仍需要审计跟踪来强制撤销并查看使用了哪些 IP:port 以及哪些凭据集。好玩的东西!
真是有趣的东西。我的想法是你仍然可以避免延迟。大多数令牌将在验证算法阶段丢弃,这就是您实施的方式。然后对已撤销的令牌进行廉价的缓存查询。那么如果有人真的坚持撤销令牌,你的情报系统应该找出来并隔离这种情况。
G
Gleno

JWT 撤销的最佳解决方案是缩短 exp 窗口、刷新并将已发布的 JWT 令牌保存在共享的近线缓存中。以 Redis 为例,这特别容易,因为您可以将缓存键设置为令牌本身(或令牌的哈希),并指定过期时间以便自动驱逐令牌。


S
Sindhu Raju

我找到了一种解决问题的方法,How to expire already generated existing JWT token using Java?

在这种情况下,我们需要使用任何数据库或内存中,

第 1 步:一旦为用户第一次生成令牌,将其与令牌一起存储在数据库中,时间为“issuedAt()”。

我以这种 JSON 格式将它存储在数据库中,

例如: {"username" : "username", "token" : "token", "issuedAt" : "issuedAt" }

第 2 步:一旦您使用要验证的令牌获取同一用户的 Web 服务请求,从令牌中获取“issuedAt()”时间戳并将其与存储的(数据库/内存中)发布的时间戳进行比较。

第 3 步:如果存储的已发布时间戳是新的(使用 after()/before() 方法),则返回令牌无效(在这种情况下,我们实际上并没有使令牌过期,但我们停止授予对该令牌的访问权限)。

这就是我解决问题的方法。


这就像在数据库中存储密码一样。不要那样做。您可以存储哈希而不是 la bcrypt、scrypt 等,但关键是要像对待密码一样对待令牌。
如果您不考虑存储用户名/令牌,这不是一种有效的方法吗?假设您只是在用户记录上存储了一个发出的时间戳,您可以使用它来使在最近一个令牌之前发布的任何先前的令牌在针对数据库进行检查时无效。唯一的缺点是只有最近的令牌有效,但您可以在定义的时间而不是每次生成令牌时在数据库中设置新的 issedAt,并且在该时间之后生成的任何令牌都将有效。
问题是关于一个自包含的令牌,而不是一个有状态的令牌。