繁体   English   中英

如何管理服务器nodejs应用程序中的身份验证令牌?

[英]How manage auth token in server nodejs application?

我必须调用一些需要传递令牌的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是单线程应用程序 - 因此调用之间没有任何同步问题。 我同意,但是如果令牌已过时且有后续调用makeHttpCall函数会怎样? 是的,一个接一个,他们将失败401并说这些电话有延迟所以订单可能是这样的:

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

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

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

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

我一直处于类似的情况,我发现处理这个问题的最理想方式是 -

  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的基本家庭滚动队列也可以承担相当大的负载。 检查公牛节点。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM