听起来你会从一个为你管理令牌的小单例对象中受益。您可以创建一个接口来获取执行以下操作的令牌:
- 如果缓存中没有相关令牌,请获取一个新令牌并返回一个将使用令牌解析的承诺。将该承诺存储在缓存中以代替令牌。
- 如果缓存中有相关令牌,请检查它是否过期。如果它已过期或即将过期,请将其删除并转到第 1 步。如果它仍然很好,则返回一个使用缓存令牌解析的承诺(这样它总是返回一个承诺,无论是否缓存)。
- 如果缓存正在获取新令牌,则缓存中将存储一个新令牌,表示新令牌的未来到达,因此缓存可以返回该承诺并解析为令牌正在提取中。
调用者的代码如下所示:
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 小时的有效期。我们如何有机制在令牌过期之前获取新令牌?
您可以在请求令牌时检查令牌是否过期,如果令牌已过期,则将现有的承诺替换为代表对令牌的新请求的承诺。