【问题标题】:How do I manage access token in Nodejs application?如何在 Nodejs 应用程序中管理访问令牌?
【发布时间】:2020-06-29 02:14:23
【问题描述】:

Click here to see Overview Diagram

大家好, 我有服务 A 需要在不同的网络域中调用服务 B。要调用服务 B,服务 A 从身份提供者获取访问令牌,然后使用 Http Authorization 标头中的访问令牌调用服务 B。当对服务 A 有多个或并发请求时,我想尽量减少对身份提供者的调用以获取访问令牌。所以我计划通过使用https://www.npmjs.com/package/lru-cache 来实现缓存,这类似于 google-auth-library 使用的方法 https://github.com/googleapis/google-auth-library-nodejs/blob/master/src/auth/jwtaccess.ts。 服务 A 将调用身份提供者以获取访问令牌并存储到缓存中。当下一个请求进来时,服务 A 会使用缓存中的令牌并调用服务 B。如果缓存项过期,则服务 A 将获取服务令牌并存储在缓存中。
我有以下问题:

  1. 当对服务 A 的并发请求可能导致发送多个请求以获取访问令牌并对缓存进行多次更新时,我们如何处理竞争条件?
  2. 比如说,访问令牌有 1 小时的有效期。我们如何有机制在令牌过期之前获取新令牌?

任何 cmets 将不胜感激。提前谢谢你。

【问题讨论】:

    标签: node.js jwt


    【解决方案1】:

    听起来你会从一个为你管理令牌的小单例对象中受益。您可以创建一个接口来获取执行以下操作的令牌:

    1. 如果缓存中没有相关令牌,请获取一个新令牌并返回一个将使用令牌解析的承诺。将该承诺存储在缓存中以代替令牌。
    2. 如果缓存中有相关令牌,请检查它是否过期。如果它已过期或即将过期,请将其删除并转到第 1 步。如果它仍然很好,则返回一个使用缓存令牌解析的承诺(这样它总是返回一个承诺,无论是否缓存)。
    3. 如果缓存正在获取新令牌,则缓存中将存储一个新令牌,表示新令牌的未来到达,因此缓存可以返回该承诺并解析为令牌正在提取中。

    调用者的代码如下所示:

    tokenCache.getToken().then(token => {
        // use token here
    });
    

    步骤 1、2 和 3 背后的所有逻辑都封装在 getToken() 方法中。


    这是tokenCache 课程的大纲,希望能给您提供大致的思路:

    const tokenExpiration = 60 * 60 * 1000;    // 1 hr in ms
    const tokenBeforeTime = 5 * 60 * 1000;     // 5 min in ms
    
    class tokenCache {
        constructor() {
            this.tokenPromise = null;
            this.timer = null;
            // go get the first token
            this._getNewToken().catch(err => {
                console.log("error fetching initial token", err);
            });
        }
        getToken() {
            if (this.tokenPromise) {
                return this.tokenPromise().then(tokenData => {
                    // if token has expired
                    if (tokenData.expires < Date.now()) {
                        return this._getNewToken();
                    } else {
                        return tokenData.token;
                    }
                });
            } else {
                return this._getNewToken();
            }
        }
    
        // non-public method for getting a new token
        _getNewToken() {
            // for example purposes, this uses the got() library to make an http request
            // you fill in however you want to contact the identity provider to get a new token
            this.tokenPromise = got(tokenURL).then(token => {
                // make resolve value be an object that contains the token and the expiration
                // set timer to get a new token automatically right before expiration
                this._scheduleTokenRefresh(tokenExpiration - tokenBeforeTime);
                return {
                    token: token,
                    expires: Date.now() + tokenExpiration;
                }
            }).catch(err => {
                // up error, clear the cached promise, log the error, keep the promise rejected
                console.log(err);
                this.tokenPromise = null;
                throw err;
            });
            return this.tokenPromise;
        }
        // schedule a call to refresh the token before it expires
        _scheduleTokenRefresh(t) {
            if (this.timer) {
                clearTimeout(this.timer);
            }
            this.timer = setTimeout(() => {
                this._getNewToken().catch(err => {
                    console.log("Error updating token before expiration", err);
                });
                this.timer = null;
            }, t);
        }
    
    }
    

    当对服务 A 的并发请求可能导致发送多个请求以获取访问令牌并对缓存进行多次更新时,我们如何处理竞争条件?

    您存储了一个承诺并始终返回该承诺。无论您是在获得一个新的令牌,还是该承诺中已经有一个令牌,都没有关系。您返回承诺,调用者在承诺上使用.then()await 来获取令牌。无论哪种方式,它都“正常工作”。

    假设访问令牌有 1 小时的有效期。我们如何有机制在令牌过期之前获取新令牌?

    您可以在请求令牌时检查令牌是否过期,如果令牌已过期,则将现有的承诺替换为代表对令牌的新请求的承诺。

    【讨论】:

    • @BanchaSetthanan - 这能回答你的问题吗?
    • 是的,您的解决方案回答了我的第一个问题。对于第二个,我希望有一种机制来主动准备对身份提供者的调用并在现有令牌过期之前获取新令牌。理想情况下,我想要一个回调函数,即 cache.onExpire(refreshToken)。
    • @BanchaSetthanan - 我添加了一些刷新逻辑。您将需要填写具体的实施细节。如果这回答了您的问题,您可以通过单击答案左侧的复选标记向社区表明这一点,这也将为您在此处遵循正确的程序赢得一些声誉积分。
    • 我想知道是否有任何库/框架可以自动完成所有这些,类似于 Microsoft.Identity.Client(它会自动获取令牌,缓存它并在必要时更新它)。
    • 顺便说一句,我遵循这个模式,这里是可以从权威端点获取令牌和其他一些信息(迪斯科)的包。如果有人感兴趣,请看一下,如果有想法可以添加 - 欢迎 PR npmjs.com/package/oidc-utils-nestjs
    猜你喜欢
    • 1970-01-01
    • 2019-09-15
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 2012-05-28
    • 1970-01-01
    • 2020-07-13
    相关资源
    最近更新 更多