我想知道在更改密码/注销时不使用db而使JWT无效的最佳做法。
我有以下想法通过点击用户数据库处理上述2个案例。
1.密码更改的内容,我检查存储在用户db中的密码(散列)。
2.注销时,我在用户数据库中保存了最后注销时间,因此通过比较令牌创建时间和注销时间,我可以使这种情况无效。
但是这两个案例的代价是每次用户点击api时都会点击用户数据库。任何最佳实践都表示赞赏。
更新 我不认为我们能够在不击中db的情况下使JWT无效。所以我想出了一个解决方案。我已经发布了我的回答,如果您有任何疑虑,欢迎您。
答案 0 :(得分:62)
使用无刷新令牌时
1.更改密码时:当用户更改密码时,请注意用户数据库中的更改密码时间,因此当更改密码时间大于令牌创建时间时,令牌为无效。因此剩余的会话很快就会被注销。
2.当用户注销时:当用户注销时,将令牌保存在单独的数据库中(例如:InvalidTokenDB并在令牌过期时从Db中删除令牌)。因此,用户从相应设备注销,他在其他设备中的会话保持不受干扰。
因此,在使JWT无效时,我遵循以下步骤:
关注上述方法:
使用刷新令牌时,访问令牌的有效期为1天,刷新令牌作为终生有效期
<强> 1。更改密码时:当用户更改密码时,请更改用户的刷新令牌。因此剩余的会话很快就会被注销。
<强> 2。当用户注销时:当用户注销时,将令牌保存在单独的数据库中(例如:InvalidTokenDB并在令牌过期时从Db中删除令牌)。因此,用户从相应设备注销,他在其他设备中的会话保持不受干扰。
因此,在使JWT无效时,我遵循以下步骤:
关注上述方法:
注意:虽然Hanz提出了一种方法来保护Using Refesh Token in Token-based Authentication is secured?中的刷新令牌,但我无法理解他在说什么。任何帮助表示赞赏。
所以,如果有人有好的建议,欢迎你提出意见。
<强>更新强> 我正在添加答案,因为您的应用程序不需要刷新令牌,终身有效期。这个答案由 Sudhanshu (https://stackoverflow.com/users/4062630/sudhanshu-gaur)给出。谢谢Sudhanshu。所以我相信这是最好的方法,
当不需要刷新令牌且访问令牌没有到期时:
当用户登录时,在他的用户数据库中创建一个没有到期时间的登录令牌。
因此,在使JWT无效时,请按照以下步骤进行操作
因此,使用这种方法,您不需要在数据库中存储注销令牌,直到它们到期,也不需要在更改上述情况下所需的密码时存储令牌创建时间。但是我相信这种方法只有在您的应用程序具有不需要刷新令牌并且令牌没有到期的要求时才会有效。
如果有人关注这种方法,请告诉我。欢迎您的意见:)
答案 1 :(得分:14)
我不知道如果不以某种方式涉及数据库而任意使令牌无效。
如果可以在多个设备上访问您的服务,请注意方法2。请考虑以下情况...
- 用户使用iPad登录,Token 1已发布并存储。
- 用户登录网站。令牌2发行。用户退出。
- 用户尝试使用iPad,在用户从网站退出之前签发了令牌1,令牌1现在被视为无效。
您可能希望查看refresh tokens的概念,尽管这些也需要数据库存储。
另请参阅here有关类似问题的优秀SO讨论,特别是IanB的解决方案,可以节省一些数据库调用。
建议的解决方案 就个人而言,这就是我接近它的方式......用户进行身份验证,使用短期到期(例如15分钟)的访问令牌发布,刷新令牌有效期更长或无限期。将此刷新令牌的记录存储在数据库中。
每当用户“活动”时,每次都会发出一个新的身份验证令牌(每次有效15分钟)。如果用户超过15分钟未激活然后发出请求(因此使用过期的jwt),请检查刷新令牌的有效性。如果它有效(包括数据库检查),则发出新的身份验证令牌。
如果用户退出&#39;无论是在设备上还是通过网站,都会破坏访问刷新令牌客户端,并且重要的是撤销所使用的刷新令牌的有效性。如果用户在任何设备上更改其密码,则撤消所有刷新令牌,强制他们在访问令牌过期后再次登录。这确实留下了一个不确定的窗口。但是每次没有击中数据库都是不可避免的。
使用这种方法也可以让用户能够撤销&#39;如果需要,可以访问特定设备,如许多主要网络应用程序所示。
答案 2 :(得分:7)
我不确定我是否在这里遗漏了一些东西,但我发现接受的答案比必要的更复杂。
我看到必须点击db才能为每个api请求验证或使令牌无效,但是总体过程可能比我在这里看到的更简单。
每当创建一个jwt时,即在登录或更改/重置密码期间,将带有用户ID的jwt插入表中并为每个jwt维护一个jti(基本上是一个uuid号)。同样的jti也进入jwt有效载荷。有效地jti唯一地标识一个jwt。当从多个设备或浏览器访问帐户时,用户可以同时拥有多个jwts,在这种情况下,jti可以区分设备或用户代理。
所以表模式是,jti |用户身份。 (以及课程的主键)
对于每个api,检查jti是否在表中,这意味着jwt是有效的。
当用户更改或重置密码时,从db中删除该userId的所有jti。创建一个带有新jti的新jwt并将其插入表中。除了更改或重置密码的会话外,这将使所有其他设备和浏览器的所有会话无效。
当用户注销时,删除该用户的特定jti但不是全部。将有一个单一登录但没有一个登出。因此,当用户注销时,他不应该从所有设备注销。但是,删除所有jtis也会从所有设备注销。
所以这将是一个表,没有日期比较。如果使用或不使用刷新令牌,情况也是如此。
然而,为了最大限度地减少数据库干扰和可能的延迟,缓存使用肯定有助于减少处理时间。
注意:请说明您是否正在投票。
答案 3 :(得分:1)
如果用户正在更改密码,您将在那里点击数据库。但是不想点击db进行授权?
我发现存储每个用户字符串的好处,并且全局共享字符串散列在一起,为我们的JWT实现提供了最大的灵活性。在这种特殊情况下,我会存储密码的散列以与全局字符串一起使用,并将它们一起散列为JWT秘密。
答案 4 :(得分:-1)
我完全同意@gopinath的回答只是想添加一件事,你应该删除所有令牌过期的更改密码时间,例如假设你已经为每个令牌设置3天到期时间而不是仅仅在数据库中正常保存更改密码时间,你也可以将其到期时间设置为3天,因为在此之前显然令牌已经过期,因此无需再次检查每个令牌是否其到期时间更长,然后更改密码时间