如何在Node.js应用程序中管理访问令牌? [英] How do I manage access token in Nodejs application?

查看:67
本文介绍了如何在Node.js应用程序中管理访问令牌?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

点击此处查看概览图

大家好, 我有服务A,需要在其他网络域中调用服务B.要调用服务B,服务A从身份提供者获取访问令牌,然后使用Http授权标头中的访问令牌调用服务B.当有多个或并发的对服务A的请求时,我想最小化对身份提供者的调用以获取访问令牌.因此,我计划使用 https://www.npmjs.com/package/lru-缓存,类似于google-auth-library使用的方法 https://github.com/googleapis/google-auth-library-nodejs/blob/master/src/auth/jwtaccess.ts . 服务A将调用身份提供者以获取访问令牌并将其存储到缓存中.当下一个请求进入时,服务A将使用缓存中的令牌并调用服务B.如果缓存项已过期,则服务A将获取服务令牌并存储在缓存中.
我有以下问题:

Hi All, I have service A that needs to call service B in different network domain. To make a call to service B, service A gets access token from identity provider then call service B with the access token in Http Authorization header. When there are multiple or concurrent requests to service A, I want to minimize the calls to identity provider to get access token. So I plan to implement caching by using https://www.npmjs.com/package/lru-cache which is similar to the approach using by google-auth-library https://github.com/googleapis/google-auth-library-nodejs/blob/master/src/auth/jwtaccess.ts. The service A will call identity provider to get access token and store to the cache. When the next request come in, the service A will use the token from cache and calls service B. If the cache item is expired, then service A will get service token and store in cache.
I have the following questions:

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

任何评论将不胜感激.预先谢谢你.

Any comments would be very appreciated. Thank you in advance.

推荐答案

听起来像您将从一个为您管理令牌的小型Singleton对象中受益.您可以创建一个接口来获取执行以下操作的令牌:

It sounds like you would benefit from a little singleton object that manages the token for you. You can create an interface for getting the token that does the following:

  1. 如果高速缓存中没有相关令牌,请获取一个新令牌并返回一个将与令牌一起解析的promise.将该承诺存储在缓存中以代替令牌.
  2. 如果高速缓存中有相关令牌,请检查令牌是否已过期.如果它已过期或将要过期,请将其删除并转到步骤1.如果它仍然很好,则返回一个使用缓存令牌解析的承诺(这样,无论是否缓存,它始终会返回一个承诺).
  3. 如果缓存正在获取新令牌,则缓存中将存储一个表示新令牌将来到来的新鲜令牌,因此缓存可以只返回该承诺并将其解析为令牌正在获取中.

呼叫者的代码如下:

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

步骤1、2和3后面的所有逻辑都封装在getToken()方法内部.

All the logic behind steps 1, 2 and 3 is encapsulated inside the getToken() method.

以下是tokenCache类的概述,希望可以为您提供总体思路:

Here's an outline for a tokenCache class that hopefully gives you the general idea:

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的并发请求可能导致发送多个请求以获取访问令牌并对缓存进行多次更新时,我们如何处理竞争条件?

How do we handle race condition when there are concurrent request to service A that can cause multiple requests are sent to get access token and have multiple updates to the cache?

您存储一个承诺,并始终返回该承诺.无论您是在获取新令牌还是在该承诺中已经有令牌,这都没有关系.您返回诺言,并且调用方在诺言上使用.then()await来获取令牌.无论哪种方式,它都起作用".

You store a promise and always return that promise. Whether you're in the middle of getting a new token or there's already a token in that promise, it doesn't matter. You return the promise and the caller uses .then() or await on the promise to get the token. It "just works" either way.

比方说,访问令牌的有效期为1小时.我们如何拥有在令牌过期之前获取新令牌的机制?

Let say, access token have 1 hour expiry. How do we have mechanism to get a new token before the token is expired?

您可以在请求令牌时检查令牌是否到期,如果令牌已过期,则可以用表示新的令牌请求的令牌替换现有的promise.

You can check the token for expiration when it's requested and if it's expired, you replace the existing promise with one that represents a new request for the token.

这篇关于如何在Node.js应用程序中管理访问令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆