【问题标题】:How should I store a token generated by a RESTful API?我应该如何存储由 RESTful API 生成的令牌?
【发布时间】:2015-04-24 20:13:18
【问题描述】:

我已经构建了一个 API,为登录的用户生成身份验证令牌。此时我也有一个用 Node.JS 编写的客户端应用程序。
当我使用用户凭据从客户端应用程序向 API 发出请求时,我得到了身份验证令牌:我应该如何将它存储在客户端应用程序中?我不应该在每次想要请求 API 时都请求令牌,对吗?
我考虑过将令牌放入 Cookie 中,但我认为这不是最好的解决方案。 你会推荐什么?

【问题讨论】:

  • 是您的客户端应用程序,它是一个 node.js 服务器,使用任何数据存储?
  • 您可以创建一个内存数据存储,如 redis 来存储令牌并将其用于进一步的请求。将会话存储与 connect-redis 结合使用是一种可行的解决方案。

标签: node.js api rest authentication token


【解决方案1】:

我们正在开发一个使用非常相似的方法的应用程序。客户端应用程序是静态 HTML5/JS 单页应用程序(没有任何服务器端生成)并与 API 服务器通信。

最好的方法是将会话令牌存储在内存中:即在 JS 代码中的一个变量中。如果您的客户端应用程序是单页,那应该不是问题。
除此之外,我们还将会话令牌保存在 sessionStorage 中,以防用户刷新页面。为了在创建新选项卡时保留令牌(sessionStorage 特定于浏览器窗口),我们还将在页面关闭时将其存储在 localStorage 中,以及打开选项卡的计数器(当应用程序的所有选项卡都关闭时,我们删除令牌。

// Handle page reloads using sessionStorage
var sess = sessionStorage.getItem('session-token')
if(sess && sess !== 'null') { // Sometimes empty values are a string "null"
    localStorage.setItem('session-token', sess)
}

// Set a counter to check when all pages/tabs of the application are closed
var counter = parseInt(localStorage.getItem('session-counter') || 0, 10)
counter++
localStorage.setItem('session-counter', counter)

// Event fired when the page/tab is closing
window.onbeforeunload = function() {
    var counter = parseInt(localStorage.getItem('session-counter') || 0, 10)
    counter--
    localStorage.setItem('session-counter', counter)

    // All pages are closed: remove the session token
    if(counter <= 0) {
        // Handle page reloads using sessionStorage
        sessionStorage.setItem('session-token', localStorage.getItem('session-token'))

        localStorage.removeItem('session-token')
    }
}

有关 localStorage 和 sessionStorage 的更多信息:https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API

为什么不使用 cookie? cookie 不好有两个原因: 1. 它们通常更持久,在浏览器窗口和选项卡之间共享,即使在浏览器关闭后它们也可以持久存在。 2. 然而,最重要的是,根据 HTTP 规范,它们必须在每次发出请求时发送到 Web 服务器。如果您正在设计一个客户端与 API 服务器完全分离的应用程序,那么无论如何您都不希望客户端的服务器看到(或记录!)会话令牌。

一些额外的建议:

  1. 会话令牌必须过期。您可以通过将会话令牌存储在服务器上的数据库中并在每个请求上验证它们和/或“签名”它们来实现这一点(以纯文本形式向令牌添加时间戳,然后添加签名部分,例如 HMAC 哈希,使用您只知道的密钥编码的时间戳)。
  2. 令牌可以在其生命周期内多次重复使用。但是,在一定秒数后,您可能希望服务器刷新令牌,使旧令牌无效并向客户端发送新令牌。

【讨论】:

    【解决方案2】:

    我认为这个链接可以帮助你:

    其实你应该有一个有过期日期的token,这样你就不必每次发送请求前都获取一个新的token。当令牌过期时,您只需从服务“刷新令牌”中获取一个新令牌。

    关于如何将令牌存储在客户端应用程序中的问题,我认为您可以将其保存在内存中(地图或嵌入式数据库)。

    否则,我认为在这种用例中使用 cookie 不是一个好主意。

    希望对您有所帮助。 蒂埃里

    【讨论】:

      【解决方案3】:

      成功登录后,应在服务器端创建一个唯一的一次性令牌,并根据用户 ID 和时间戳存储在数据库中。您将令牌存储在 cookie 客户端中。然后,您将令牌传递给每个后续 API 调用。然后服务器应检查令牌是否有效(即未过期,例如在 30 分钟前发布或更新)。如果它有效,您可以检索针对该令牌存储的用户详细信息并执行您需要的任何后端功能(当用户通过身份验证时)。然后,您更新该令牌的时间戳(刷新会话,因为您希望在 30 分钟没有用户交互后登录超时)。如果在获取 API 调用时令牌已过期或不存在,则重定向到登录页面。

      此外,您可能已经知道这一点,但请确保令牌是唯一的且不可猜测的,我倾向于生成新的随机 GUID 并对其进行加密,不要使用顺序 id 或类似的东西。

      【讨论】:

      • 是的,我知道独特且不可猜测的部分,但无论如何感谢您指出。我对 cookie 方法的问题是,它安全吗?是不是可以窃取cookie值并访问其他用户的隐私信息?
      • 假设您使用的是 SSL,那么使用中间人攻击将很难截取该值。使用唯一的过期令牌会使令牌在使用后变得无用,因此相对安全。假设用户在会话期间(可能)保持相同的 ip,您还可以对令牌进行 ip 验证。但是,是的,客户端上可能存在将所有 cookie 信息上传到第三个站点的恶意代码,但我想说,如果正确实施,对于大多数 Web 应用程序来说,使用 cookie 是足够安全的。或者,您可以查看 OAuth,这是另一个令人头疼但被广泛使用的身份验证方法。
      • 实际上,我正在使用 OAuth2.0 来验证凭据并生成令牌。但我仍然需要将令牌存储在客户端。除非,我弄错了!
      • 以下问题与此问题类似,答案和评论与我的类似,并说明使用 cookie 的风险很小,只要令牌是一次性的。 stackoverflow.com/questions/1283594/… 大多数网站都这样做,您需要将令牌存储在某处。使用客户端代码的地方就是 cookie。
      • 我建议不要将令牌存储在 cookie 中,因为它会根据任何请求传输到服务器。而且我认为会话生命不应该更新:让它过期并刷新它!
      猜你喜欢
      • 1970-01-01
      • 2018-07-24
      • 2013-02-10
      • 2023-03-26
      • 2012-03-10
      • 2019-05-12
      • 2013-01-12
      • 2021-02-27
      相关资源
      最近更新 更多