【问题标题】:How manage auth token in server nodejs application?如何在服务器 nodejs 应用程序中管理身份验证令牌?
【发布时间】:2019-05-25 06:33:52
【问题描述】:

我必须调用一些需要传递令牌的 api,并且需要刷新此令牌,主要问题是 - 如何以及在服务器中存储令牌的位置? 互联网中的一些解决方案告诉做某事像那样(代码可能不起作用,但想法很明显):

let myToken = { ... }
class MyService {
    myFunc() {
        makeHttpCall(myToken).catch(async err => {
            if (err.httpCode == 401) {
                let netToken = await refreshToken()
                myToken = netToken;
                return myFunc();
            }

        })
    }
 }
module.exports = MyService

他们告诉 nodejs 是单线程应用程序 - 所以调用之间的同步没有任何问题。我同意, 但是如果令牌已过时并且有 2 个后续调用 makeHttpCall 函数怎么办?是的,他们将一一失败 401 假设这些电话有延迟,所以订单可能是这样的:

    First invocation                    
           |                                 
          \|/                                
      makeHttpCall                      Second invocation
           |                                     | 
          \|/                                   \|/ 
          401                               makeHttpCall
           |                                     |    
          \|/                                   \|/  
        refresh (unexpected delay here)         401  
           |                                     |   
           |                                    \|/  
           |                                  refresh  
          \|/                                    |
     writeNewToken                               |
           |                                     |
          \|/                                    |
       makeHttpCall                              |
           |                                     |       
          \|/                                   \|/ 
          401                             writeNewToken
(because token refreshed twice)                    

所以想法是 - 第二个 makeHttpCall 在从第二次调用收到新令牌后立即发出请求。

如何同步?如何刷新令牌并确保它没有被覆盖?

请注意,该问题与任何用户会话或 oauth 令牌无关。带有会话或签署 oauth 的解决方案不是答案

【问题讨论】:

  • 一旦一个令牌被刷新,旧的就失效了。恕我直言,什么都不需要做。
  • 如果有的话,就是前端做错了。

标签: javascript node.js token


【解决方案1】:

我也遇到过类似的情况,我找到的最理想的处理方法是 -

  1. 将所有内容包装到一个主函数中,该函数负责刷新令牌和分派请求。

  2. 请求应该只完成获取数据的任务,如果被拒绝则优雅地失败,让主函数知道令牌不再有效,但不会尝试刷新令牌本身。

  3. 存储刷新令牌,说明当前是否正在刷新以及已分派的请求。在原型/小型系统中,您可以将其存储在内存中,但我强烈建议使用持久性数据存储 - sqlite、postgresql、mysql 或启用持久性的 redis。

请求永远不应该尝试刷新令牌本身,因为在高吞吐量环境中,您很快就会处于每次请求调度都会刷新令牌的状态。如果一个新的令牌立即使之前的令牌失效,你会看到请求成功或被随机拒绝

以编程方式(伪代码)-

let refreshToken = 'asfasfjhskajfaskf',
       isTokenBeingRefreshed = 0, //1 if it is indeed being refreshed
       pendingRequests = {      
          "<request id>":"data"
       }

function dispatchRequest(params) {       
  if(isTokenBeingRefreshed) {
     //push requests into pendingRequests and wait for token to be refreshed

  if(!isTokenBeingRefreshed) {
    //dispatch request
  }

}

收到分派请求后,请检查令牌是否正在刷新。如果是,请保留请求。如果不是,则发送请求。如果请求因身份验证错误而失败,则暂停所有其他请求(如果已经分派,它们将失败但不要请求另一个令牌,因为一个令牌已经在进行中,只需默默地确认刷新令牌的请求)和等待令牌刷新。请区分请求是否被拒绝、由于操作错误、网络错误等而失败。

您可能必须使用 setInterval 之类的方法来调度暂停的请求。

如果您将此作为项目的一部分编写,该项目将是长期且可能具有高吞吐量的项目,那么您应该使用消息代理或队列进行评估。要考虑的选项是 Kafka、ZeroMQ、RabbitMQ。即使是使用 Redis 的基本家庭滚动队列也可以完成相当大的负载。检查到bull 的节点。

注意 - 在 pendingRequests 中,最好存储所有未决、已完成和失败的请求及其数据。为每个请求分配一个唯一的 id 也是一个好主意,这样如果由于身份验证错误或网络错误而失败,您可以重试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-26
    • 2019-11-13
    • 1970-01-01
    • 2015-01-01
    • 2021-04-26
    • 2021-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多