在 NodeJS 中一次只允许从一台设备登录

Only allow signing in from one device at a time in NodeJS

我正在使用 JWT 进行身份验证。但是我不希望用户从多个设备登录。我如何确保这一点?

现在 - 我能想到的就是将 JWT 存储到数据库中,然后检查它是否存在。如果它存在,它是在什么时间生成的。如果时间太多 - 我们会重新生成令牌并传回第二台设备。

这几乎是您唯一的选择,JWT 是故意设计成无状态的。类似于如果没有类似技术就无法真正进行服务器端注销

正如 jfriend 指出的那样,仅存储 JWT 是不够的。您需要用它做的是确保下次用户请求登录时,他们还没有向他们发出未过期的 JWT。

完成整个流程:

情况 1:用户未在任何地方登录。在这种情况下,发布并存储 JWT。可能在用户记录中以便于检索。

情况2:用户尝试在另一台设备上登录。无论您是让他们明确退出第一个设备还是为他们这样做,您现在都必须将该存储的令牌发送到已撤销令牌列表中。在确定令牌是否有效时,您的令牌验证逻辑必须考虑该列表。

/* 进一步说明 */

我觉得更多的细节可能对人们有用,所以我将深入实施。

** 未经身份验证的请求 **

这应该不会改变,但值得一提的是,我假设您有需要身份验证的路由,并且对那些不包含有效 JWT 的路由的请求会被 401 拒绝(并且可能提供URL 到登录名 url)。

登录

登录逻辑始终包含用户查找,因此如上所述,此应用程序中的流程应包含该查找,但在将用户登录到应用程序之前,您将检查是否已为用户分配了令牌尚未过期。

如果没有已分配给用户的令牌,则按照您通常的方式检查凭据,生成 JWT(带有 exp 标题以指示有效负载中的过期时间),将该令牌保存回用户 document/record 供以后参考。

如果 分配的令牌也未过期,那么您要么必须将用户从另一台设备注销(稍后会详细介绍),然后登录他们进入当前设备,否则您必须拒绝登录尝试并让该人退出新设备。我认为前一种方法对用户更友好,但这取决于您的应用程序的需求。

注销

对于 JWT,保证用户无法使用已颁发令牌的唯一方法是在有效负载中包含过期时间 (exp),并使用验证程序来检查该时间,或者在服务器上知道哪些令牌不再有效并检查它们。最强大的解决方案可以同时做到这两点。

因此,假设您已经在处理过期,显式注销功能将通过在某处创建一个已撤销的令牌列表来处理。例如,如果您使用 MongoDB,您将创​​建一个 collection 来存储它们。理想情况下,您还可以为每个设置为到期日期后某个时间点的令牌设置一个 TTL,这样 Mongo 将驱逐无论如何都会过期的令牌,以节省您自己的时间和 space。

如果您在新登录请求上执行 auto-logout,当您将新令牌保存在用户文档中时,您将点击此逻辑将旧令牌放入已撤销令牌列表。

经过身份验证的用户也应该可以访问注销路由,以便在他们需要时明确注销,无论您是否执行自动注销。

已验证请求

此时您应该可以合理确定用户只能在一台设备上登录。但是,您还需要确保他们没有尝试使用已撤销的令牌发出请求。

然后您的通用路由安全中间件还需要检查已撤销的令牌列表,以查看客户端提供的令牌在检查后是否在所述列表中以确保它没有过期(因为可以在验证时检查过期, 将 round-trip 保存到数据库。

我完全同意@Prateek Narendra。 一旦用户登录,将其令牌存储在数据库中(我将其存储在 activeJWT 字段中),然后在进一步登录时检查 activeJWT 是否为空。 如果是,则允许登录,否则不允许。 在注销时,您需要使 activeJWT 字段为空。 您可以为此解码当前令牌。

注:我不是auth专家

  • 我认为您应该将 last_login 日期时间保存在数据库中。

  • 将 JWT 过期设置为 7 天。为每个请求更新 JWT 令牌(例如用户请求 /user 此端点每次都在此处发送新的 JWT 令牌。)

  • 注销时将 last_login 设置为 null

  • 当有人尝试登录时 检查 last_login 字段,如果它是 nulllast_login date-time 早于 7 天 然后处理否则 首先显示一条消息以注销。