Javascript 在 node.js 中更改密码和注销时使 JWT 无效的最佳实践?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/28759590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best practices to invalidate JWT while changing passwords and logout in node.js?
提问by Gopinath Shiva
I would like to know the best practices to invalidate JWT without hitting db while changing password/logout.
我想知道在更改密码/注销时不点击 db 使 JWT 无效的最佳实践。
I have the idea below to handle above 2 cases by hitting the user database.
我的想法是通过点击用户数据库来处理以上 2 种情况。
1.Incase of password changes, I check for password(hashed) stored in the user db.
1.Incase 密码更改,我检查密码(散列)存储在用户数据库中。
2.Incase of logout, I save last-logout time in user db, hence by comparing the token created time and logout time, I can able to invalidate this case.
2.在注销的情况下,我将上次注销时间保存在用户数据库中,因此通过比较令牌创建时间和注销时间,我可以使这种情况无效。
But these 2 cases comes at the cost of hitting user db everytime when the user hits the api. Any best practise is appreciated.
但这两种情况的代价是每次用户点击 api 时都会点击用户数据库。任何最佳实践表示赞赏。
UPDATE:I dont think we can able to invalidate JWT without hitting db. So I came up with a solution. I have posted my answer, if you have any concern, you are welcome.
更新:我认为我们无法在不点击 db 的情况下使 JWT 失效。所以我想出了一个解决方案。我已经发布了我的答案,如果您有任何疑问,欢迎您。
回答by Gopinath Shiva
When No Refresh token is used:
不使用刷新令牌时:
1.While changing password:when the user changes his password, note the change password time in the user db, so when the change password time is greater than the token creation time, then token is not valid. Hence the remaining session will get logged out soon.
1.修改密码时:用户修改密码时,注意用户数据库中的修改密码时间,所以当修改密码时间大于令牌创建时间时,令牌无效。因此,剩余的会话将很快注销。
2.When User logs out:When the user logs out, save the token in a seperate DB (say: InvalidTokenDB and remove the token from Db when token expires). Hence user logs out from the respective device, his sessions in other device left undisturbed.
2.当用户注销时:当用户注销时,将令牌保存在单独的数据库中(例如:InvalidTokenDB 并在令牌过期时从数据库中删除令牌)。因此,用户从相应设备注销,他在其他设备中的会话不受干扰。
Hence while invalidating a JWT, I follow the below steps:
因此,在使 JWT 无效时,我遵循以下步骤:
- Check whether the token is valid or not.
- If valid, check it is present in invalidTokenDB (a database where logged out tokens are stored till their expiry time).
- If its not present, then check the token created time and changed password time in user db.
- If changed password time < token created time, then token is valid.
- 检查令牌是否有效。
- 如果有效,请检查它是否存在于 invalidTokenDB(一个数据库,其中存储了注销的令牌,直到它们到期)。
- 如果它不存在,则检查用户 db 中的令牌创建时间和更改密码时间。
- 如果更改的密码时间 < 令牌创建时间,则令牌有效。
Concern with the above method:
对上述方法的关注:
- For each api request, I need to follow all the above steps, which might affect performance.
- 对于每个api请求,我需要按照上述所有步骤操作,这可能会影响性能。
When Refresh token is used:with expiry of access token as 1 day, refresh token as lifetime validity
使用刷新令牌时:访问令牌的有效期为 1 天,刷新令牌为终生有效期
1. While changing password:When the user changes his password, change the refresh token of the user. Hence the remaining session will get logged out soon.
1. 更改密码时:当用户更改密码时,更改用户的刷新令牌。因此,剩余的会话将很快注销。
2. When User logs out: When the user logs out, save the token in a seperate DB (say: InvalidTokenDB and remove the token from Db when token expires). Hence user logs out from the respective device, his sessions in other device left undisturbed.
2. 当用户注销时:当用户注销时,将令牌保存在单独的数据库中(例如:InvalidTokenDB 并在令牌过期时从 Db 中删除令牌)。因此,用户从相应设备注销,他在其他设备中的会话不受干扰。
Hence while invalidating a JWT, I follow the below steps:
因此,在使 JWT 无效时,我遵循以下步骤:
- check whether the token is valid or not
- If valid, check whether the token is present in InvalidTokenDB.
- If not present, check the refresh token with the refresh token in userDB.
- If equals, then its a valid token
- 检查令牌是否有效
- 如果有效,检查令牌是否存在于 InvalidTokenDB 中。
- 如果不存在,请使用 userDB 中的刷新令牌检查刷新令牌。
- 如果等于,则它是一个有效的令牌
Concern with the above method:
对上述方法的关注:
- For each api request, I need to follow all the above steps, which might affect performance.
- How do I invalidate the refresh token, as refresh token has no validity, if its used by hacker, still the authentication is valid one, request will be success always.
- 对于每个api请求,我需要按照上述所有步骤操作,这可能会影响性能。
- 我如何使刷新令牌无效,因为刷新令牌无效,如果它被黑客使用,身份验证仍然有效,请求将始终成功。
Note: Although Hanz suggested a way to secure refresh token in Using Refesh Token in Token-based Authentication is secured?, I couldn't able to understand what he is saying. Any help is appreciated.
注意:尽管 Hanz在基于令牌的身份验证中使用 Refesh 令牌中建议了一种保护刷新令牌的方法是否安全?,我无法理解他在说什么。任何帮助表示赞赏。
So If anyone have nice suggestion, your comments are welcome.
所以如果有人有好的建议,欢迎您的评论。
UPDATE:I am adding the answer incase your app needs no refresh token with lifetime expiry. This answer was given by Sudhanshu(https://stackoverflow.com/users/4062630/sudhanshu-gaur). Thanks Sudhanshu. So I believe this is the best way to do this,
更新:我正在添加答案,以防您的应用程序不需要生命周期到期的刷新令牌。这个答案是由Sudhanshu( https://stackoverflow.com/users/4062630/sudhanshu-gaur)给出的。谢谢苏丹舒。所以我相信这是最好的方法,
When No Refresh token needed and no expiry of access tokens:
当不需要刷新令牌且访问令牌没有到期时:
when user login, create a login token in his user database with no expiry time.
当用户登录时,在他的用户数据库中创建一个没有过期时间的登录令牌。
Hence while invalidating a JWT, follow the below steps,
因此,在使 JWT 无效时,请按照以下步骤操作,
- retrieve the user info and Check whether the token is in his User database. If so allow.
- When user logs out, remove only this token from his user database.
- When user changes his password, remove all tokens from his user database and ask him to login again.
- 检索用户信息并检查令牌是否在他的用户数据库中。如果允许。
- 当用户注销时,仅从其用户数据库中删除此令牌。
- 当用户更改他的密码时,从他的用户数据库中删除所有令牌并要求他再次登录。
So with this approach, you don't need to store neither logout tokens in database until their expiry nor storing token creation time while changing password which was needed in the above cases. However I believe this approach only valids if your app has requirements with no refresh token needed and no expiry of the tokens.
因此,使用这种方法,您既不需要在数据库中存储注销令牌,直到它们到期,也不需要在更改上述情况下所需的密码时存储令牌创建时间。但是,我相信这种方法仅在您的应用程序具有不需要刷新令牌且令牌没有到期的要求时才有效。
If anyone has concern with this approach, please let me know. Your comments are welcome :)
如果有人对此方法有疑问,请告诉我。欢迎您提出意见:)
回答by DevFox
There is no way I know of to arbitrarily invalidate a token without involving a database one way or another.
我不知道在不涉及数据库的情况下以一种或另一种方式任意使令牌无效。
Be careful with Approach 2 if your service can be accessed on several devices. Consider the following scenario...
如果您的服务可以在多个设备上访问,请注意方法 2。考虑以下场景...
- User signs in with iPad, Token 1 issued and stored.
- User signs in on website. Token 2 issued. User logs out.
- User tries to use iPad, Token 1 was issued before user logged out from website, Token 1 now considered invalid.
- 用户使用 iPad 登录,令牌 1 已发行并存储。
- 用户登录网站。代币 2 发行。用户注销。
- 用户尝试使用 iPad,令牌 1 在用户退出网站之前发出,令牌 1 现在被视为无效。
You might want to look at the idea of refresh tokensalthough these require database storage too.
您可能想看看刷新令牌的想法,尽管它们也需要数据库存储。
Also see herefor a good SO discussion regarding a similar problem, particular IanB's solution which would save some db calls.
另请参阅此处有关类似问题的良好 SO 讨论,特别是 IanB 的解决方案,它可以节省一些 db 调用。
Proposed solutionPersonally, this is how I'd approach it...user authenticates, issued with access token with a short expiry (say 15 mins) and a refresh token valid either for a much longer period or indefinitely. Store a record of this refresh token in a db.
建议的解决方案就我个人而言,这就是我的处理方式...用户身份验证,颁发的访问令牌有效期很短(比如 15 分钟)和一个有效期更长或无限期的刷新令牌。将此刷新令牌的记录存储在数据库中。
Whenever the user is 'active', issue a new auth token each time (valid for 15 mins each time). If the user is not active for over 15 minutes and then makes a request (so uses an expired jwt), check the validity of the refresh token. If it's valid (including db check) then issue a new auth token.
每当用户处于“活跃”状态时,每次都发出一个新的身份验证令牌(每次有效期为 15 分钟)。如果用户超过 15 分钟未处于活动状态,然后发出请求(因此使用过期的 jwt),请检查刷新令牌的有效性。如果它有效(包括数据库检查),则发出一个新的身份验证令牌。
If a user 'logs out' either on a device or through a website then destroy both access refresh tokens client side and importantly revoke the validity of the refresh token used. If a user changes their password on any device, then revoke all their refresh tokens forcing them to log in again as soon as their access token expires. This does leave a 'window of uncertainty' but that's unavoidable without hitting a db every time.
如果用户在设备上或通过网站“注销”,则销毁两个访问刷新令牌客户端,重要的是撤销所使用的刷新令牌的有效性。如果用户在任何设备上更改了他们的密码,那么一旦他们的访问令牌过期,就会撤销所有刷新令牌,迫使他们再次登录。这确实留下了“不确定性窗口”,但如果每次都没有击中 db,这是不可避免的。
Using this approach also opens up the possibility of users being able to 'revoke' access to specific devices if required as seen with many major web apps.
使用这种方法还使用户能够在需要时“撤销”对特定设备的访问,就像许多主要的网络应用程序一样。
回答by Amruta-Pani
I am not sure if I'm missing something here but I find that the accepted answer is more complicated than is necessary.
我不确定我是否在这里遗漏了什么,但我发现接受的答案比必要的要复杂。
I see that db has to be hit to validate or invalidate a token for each api request, however the total process could have been simpler as I see things here.
我看到必须命中 db 才能为每个 api 请求验证或使令牌无效,但是正如我在这里看到的那样,整个过程可能会更简单。
Whenever a jwt is created, i.e. during login or change/reset password, insert the jwt with userid into a table and maintain a jti (a uuid number basically) for each jwt. The same jti goes into jwt payload too. Effectively jti uniquely identifies a jwt. A user can have multiple jwts at the same time when the account is accessed from multiple devices or browsers in which case, jti differentiates the device or the user-agent.
每当创建 jwt 时,即在登录或更改/重置密码期间,将带有 userid 的 jwt 插入表中,并为每个 jwt 维护一个 jti(基本上是一个 uuid 编号)。同样的 jti 也进入 jwt 有效载荷。有效地 jti 唯一标识一个 jwt。当从多个设备或浏览器访问帐户时,用户可以同时拥有多个 jwt,在这种情况下,jti 区分设备或用户代理。
So the table schema would be, jti | userId. (and a primary key ofcourse)
所以表模式将是, jti | 用户身份。(当然还有主键)
For each api, check if the jti is in the table, which means the jwt is a valid one.
对于每个api,检查jti是否在表中,这意味着jwt是有效的。
When the user changes or resets the password, delete all the jti of that userId from the db. Create and insert a new jwt with a new jti into the table. This will invalidate all the sessions from all other devices and browsers except the one that changed or reset the password.
当用户更改或重置密码时,从数据库中删除该 userId 的所有 jti。创建一个带有新 jti 的新 jwt 并将其插入到表中。这将使所有其他设备和浏览器的所有会话无效,但更改或重置密码的会话除外。
When the user logsout, delete that particular jti of that user but not all. There would be a Single Login but not a single Logout. So when the user logs out, he shouldnt be logged out from all the devices. However, deleting all the jtis would logout from all the devices too.
当用户注销时,删除该用户的特定 jti,但不是全部。会有单次登录,但不会有单次登出。所以当用户注销时,他不应该从所有设备上注销。但是,删除所有 jtis 也会从所有设备中注销。
So it would be one table and no date comparisons. Also it would be the same case if a refresh token is used or not.
所以它将是一张表,没有日期比较。是否使用刷新令牌也是同样的情况。
However to minimize the db interference, and possible delays, cache usage would certainly help to ease things on processing time front.
然而,为了尽量减少数据库干扰和可能的延迟,缓存使用肯定有助于缓解处理时间方面的问题。
Note: Please reason if you are down voting it.
注意:如果您拒绝投票,请说明原因。
回答by Mark Essel
If a user is changing their password, you're going to hit the db there. But don't want to hit the db for authorization?
如果用户正在更改他们的密码,您将点击那里的数据库。但是不想点击数据库进行授权?
I have found the benefits of storing a per user string, and a global shared string hashed together gives us the most flexibility with our JWT implementation. In this particular case I'd store a hash of the password to use with the global string and hash them together for a JWT secret.
我发现存储每个用户字符串的好处,并且散列在一起的全局共享字符串为我们的 JWT 实现提供了最大的灵活性。在这种特殊情况下,我将存储密码的散列以与全局字符串一起使用,并将它们散列在一起以获得 JWT 机密。
回答by Sudhanshu Gaur
I agree solely with @gopinath answer just want to add one thing that you should also remove the change password time when all of your tokens expired for example suppose you have set 3 day expiry time for every token to expire now instead of just normaly saving change password time in database you can also set its expiry time of 3 days because as obviously tokens before this will be expired so no need to check for every token again that whether its expiry time is greater then change password time or not
我完全同意@gopinath 的回答只是想添加一件事,当所有令牌过期时,您还应该删除更改密码时间,例如假设您为每个令牌设置了 3 天的过期时间,现在就过期而不是正常保存更改数据库中的密码时间您还可以将其过期时间设置为 3 天,因为在此之前的令牌显然会过期,因此无需再次检查每个令牌是否其过期时间大于更改密码时间

