Как мне управлять токеном доступа в приложении Nodejs? - PullRequest
0 голосов
/ 18 марта 2020

Нажмите здесь, чтобы увидеть обзорную диаграмму

Привет всем, у меня есть служба A, которой требуется вызвать службу B в другом домене сети. Для вызова службы B служба A получает токен доступа от провайдера идентификации, а затем вызывает службу B с токеном доступа в заголовке авторизации Http. При наличии нескольких или одновременных запросов к службе 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 час истечения. Как у нас есть механизм для получения нового токена до его истечения?

Любые комментарии будут очень благодарны. Заранее спасибо.

1 Ответ

0 голосов
/ 18 марта 2020

Похоже, вы бы выиграли от маленького одноэлементного объекта, который управляет токеном для вас. Вы можете создать интерфейс для получения токена, который выполняет следующие действия:

  1. Если в кэше нет соответствующего токена, go получает новый токен и возвращает обещание, которое будет разрешено с токеном. Храните это обещание в кэше вместо токена.
  2. Если в кэше есть соответствующий токен, проверьте срок его действия. Если срок его действия истек или он истекает, удалите его и go к шагу 1. Если это все еще хорошо, верните обещание, которое разрешается с помощью кэшированного токена (таким образом, оно всегда возвращает обещание, независимо от того, кэшировано оно или нет).
  3. Если кэш находится в процессе получения нового токена, в кэше будет храниться токен fre sh, который представляет будущее поступление нового токена, поэтому кэш может просто вернуть это обещание и преобразуется в токен, который находится в процессе выборки.

Код вызывающего абонента будет выглядеть следующим образом:

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

Все логики c за шагами 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 час истечения. Как у нас есть механизм для получения нового токена до его истечения?

Вы можете проверить токен на срок действия, когда он запрашивается, и если срок его действия истек, вы заменяете существующее обещание на то, которое представляет новый запрос на токен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...