我有一个 Api 和一个移动客户端。我使用刷新令牌如下:
问题: 假设用户有 2 个设备,A 和 B。他使用设备 A 成功登录,因此他获得了 2 个令牌,他很高兴。一旦用户从设备 B 登录,Api 就会向他发送一对新的令牌,这意味着新的刷新令牌将覆盖设备 A 已经给定的令牌。现在用户返回设备 A aaa,它就消失了(无效刷新)令牌!)所以他必须再次提供凭据,而我、用户和您都不想这样做。
建议: 我找到了两种解决问题的方法,但因为我对这个主题还很陌生,所以我看不出哪种是最佳实践:
方法A: 为每个用户保存多个刷新令牌,并保存设备标识符(仍然不知道在移动、浏览器和桌面客户端等不同客户端类型的情况下到底是什么!)但无论如何。在此方法中,当用户从设备 A 登录时,Api 会提供两个令牌。用户从设备 B 登录,Api 会传递新的令牌对。他回到设备 A,再次使用他的第一个令牌(仍然有效)。
方法B: 每个用户保留 1 个刷新令牌。当用户从设备 B 登录时,Api 会发回唯一的刷新令牌(只要用户提供有效的凭据,我就不应该关心设备,对吧?)
您能指出每种方法的优缺点吗?
方法 A 是处理此问题的正常方法。访问/刷新令牌不应该在不同的会话/设备之间共享。 每个人都应该得到自己的一双。当您使用
refresh_token
操作时,它应该只会使与该请求一起使用的刷新令牌过期,而不是任何不相关的令牌。
每个用户数据库条目都有一个
refreshTokens
字段,它是一个
RefreshTokenObject
数组,格式如下: {token: <tokenUUID>, creationDate: Date}
登录每次用户登录时,都会将新的
RefreshTokenObject
连接到现有令牌对象列表。
令牌刷新后端对
map
条目执行
RefreshTokenObjects
,用新令牌和创建日期替换找到的 RefreshTokenObject
,将新的刷新 + 访问令牌发送给用户。如果未找到令牌,服务器将返回 401,正文中带有 refresh token expired
,提示前端重新登录。退出
每次注销发生时,前端都会将正文发布到 /api/login/signout
{ userId: <id>, refreshToken: <tokenUUID> }
。如果在列表中找到相应的令牌,后端将删除相应的令牌。注意:
我选择如果没有找到refreshToken,后端不返回404;这是一种极其奇怪的情况,只会导致在最坏的情况下重新登录。良好实践
由于许多数据库都有文档大小限制,因此您必须小心不断增长的数组。我实现了一个 clearOldTokens
creationDate
字段与当前时间进行比较,如果令牌过期,它将从用户的
refreshTokenObjects
数组中删除该令牌。
使用设备 ID 等来跟踪这一点更加明智;只是更多的工作。
假设访问令牌持续 5 分钟,刷新令牌持续 1 周。访问令牌过期后,您将检查数据库是否具有请求中包含的刷新令牌。 (您还可以清理过期的刷新令牌。我将刷新令牌存储在 MongoDB 中的一个数组中,因此在修改它时,我不妨清理它以缩短它,然后再将其放回数据库中。)
和
刷新令牌,以将刷新令牌的到期日期重置为下周(因此用户不必再次登录,除非他长时间不活动)。这意味着之前的刷新令牌不再有效/使用,需要删除。只有发出请求的设备才会知道新的刷新令牌,并且很难将此更改传达给其他设备,因此方法 B 不起作用。 (我怀疑您是否可以发送通知来更改其他设备的 cookie(使用刷新令牌)。)