[英]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的解决方案不是答案
我一直处于类似的情况,我发现处理这个问题的最理想方式是 -
将所有内容包装到主函数中,该函数负责刷新令牌并分派请求。
该请求应该只完成获取数据的任务,如果被拒绝则优雅地失败,让主函数知道令牌不再有效但不尝试刷新令牌本身。
存储刷新令牌,如果当前正在刷新,则说明已分派的请求。 在原型/小型系统中,您可以将其存储在内存中,但我强烈建议使用持久性数据存储 - 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.