使用过期令牌同时发出 API 请求时如何避免多个令牌刷新请求
How to avoid multiple token refresh requests when making simultaneous API requests with an expired token
API 使用 JWT 的请求在 flask 和 Vue.js 中实现。
JWT 存储在 cookie 中,服务器为每个请求验证 JWT。
如果令牌已过期,将返回 401 错误。
如果您收到 401 错误,请按照以下代码刷新令牌,
原来的 API 请求再次发出。
以下代码对所有请求都是通用的。
http.interceptors.response.use((response) => {
return response;
}, error => {
if (error.config && error.response && error.response.status === 401 && !error.config._retry) {
error.config._retry = true;
http
.post(
"/token/refresh",
{},
{
withCredentials: true,
headers: {
"X-CSRF-TOKEN": Vue.$cookies.get("csrf_refresh_token")
}
}
)
.then(res => {
if (res.status == 200) {
const config = error.config;
config.headers["X-CSRF-TOKEN"] = Vue.$cookies.get("csrf_access_token");
return Axios.request(error.config);
}
})
.catch(error => {
});
}
return Promise.reject(error);
});
当令牌过期同时发出多个 API 请求时
无用地刷新令牌。
例如,请求 A、B、C 几乎同时执行。
由于每次请求都会返回 401,
每个拦截器都会刷新令牌。
没有什么实质性的坏处,但我觉得不是什么好办法。
有一个很好的方法可以解决这个问题。
我的想法是先发出一个API请求来验证令牌过期,
该方法是在验证刷新完成后,再发起请求A、B、C。
因为 cookie 是 HttpOnly,所以无法在客户端验证到期日期 (JavaScript)。
抱歉英语不好...
您需要做的是在拦截器之外维护一些状态。说的是
Hold up, I'm in the middle of getting a new token.
最好保留对 Promise
的引用。这样,第一个 401 拦截器可以创建承诺,然后所有其他请求都可以等待它。
let refreshTokenPromise // this holds any in-progress token refresh requests
// I just moved this logic into its own function
const getRefreshToken = () => http.post('/token/refresh', {}, {
withCredentials: true,
headers: { 'X-CSRF-TOKEN': Vue.$cookies.get('csrf_refresh_token') }
}).then(() => Vue.$cookies.get('csrf_access_token'))
http.interceptors.response.use(r => r, error => {
if (error.config && error.response && error.response.status === 401) {
if (!refreshTokenPromise) { // check for an existing in-progress request
// if nothing is in-progress, start a new refresh token request
refreshTokenPromise = getRefreshToken().then(token => {
refreshTokenPromise = null // clear state
return token // resolve with the new token
})
}
return refreshTokenPromise.then(token => {
error.config.headers['X-CSRF-TOKEN'] = token
return http.request(error.config)
})
}
return Promise.reject(error)
})
API 使用 JWT 的请求在 flask 和 Vue.js 中实现。 JWT 存储在 cookie 中,服务器为每个请求验证 JWT。
如果令牌已过期,将返回 401 错误。 如果您收到 401 错误,请按照以下代码刷新令牌, 原来的 API 请求再次发出。 以下代码对所有请求都是通用的。
http.interceptors.response.use((response) => {
return response;
}, error => {
if (error.config && error.response && error.response.status === 401 && !error.config._retry) {
error.config._retry = true;
http
.post(
"/token/refresh",
{},
{
withCredentials: true,
headers: {
"X-CSRF-TOKEN": Vue.$cookies.get("csrf_refresh_token")
}
}
)
.then(res => {
if (res.status == 200) {
const config = error.config;
config.headers["X-CSRF-TOKEN"] = Vue.$cookies.get("csrf_access_token");
return Axios.request(error.config);
}
})
.catch(error => {
});
}
return Promise.reject(error);
});
当令牌过期同时发出多个 API 请求时 无用地刷新令牌。 例如,请求 A、B、C 几乎同时执行。 由于每次请求都会返回 401, 每个拦截器都会刷新令牌。
没有什么实质性的坏处,但我觉得不是什么好办法。 有一个很好的方法可以解决这个问题。
我的想法是先发出一个API请求来验证令牌过期, 该方法是在验证刷新完成后,再发起请求A、B、C。 因为 cookie 是 HttpOnly,所以无法在客户端验证到期日期 (JavaScript)。
抱歉英语不好...
您需要做的是在拦截器之外维护一些状态。说的是
Hold up, I'm in the middle of getting a new token.
最好保留对 Promise
的引用。这样,第一个 401 拦截器可以创建承诺,然后所有其他请求都可以等待它。
let refreshTokenPromise // this holds any in-progress token refresh requests
// I just moved this logic into its own function
const getRefreshToken = () => http.post('/token/refresh', {}, {
withCredentials: true,
headers: { 'X-CSRF-TOKEN': Vue.$cookies.get('csrf_refresh_token') }
}).then(() => Vue.$cookies.get('csrf_access_token'))
http.interceptors.response.use(r => r, error => {
if (error.config && error.response && error.response.status === 401) {
if (!refreshTokenPromise) { // check for an existing in-progress request
// if nothing is in-progress, start a new refresh token request
refreshTokenPromise = getRefreshToken().then(token => {
refreshTokenPromise = null // clear state
return token // resolve with the new token
})
}
return refreshTokenPromise.then(token => {
error.config.headers['X-CSRF-TOKEN'] = token
return http.request(error.config)
})
}
return Promise.reject(error)
})