在同时发出带有过期令牌的API请求时如何避免多个令牌刷新请求

问题描述 投票:1回答:1

使用JWT的API请求是在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)上验证到期日期。

抱歉,英语不好...

axios jwt refresh-token
1个回答
1
投票

您需要做的是在拦截器外部保持一些状态。东西说

等等,我正在获取新令牌。

最好通过保留对Promise的引用来完成。这样,第一个401拦截器可以创建承诺,然后所有其他请求都可以等待它。

const 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)
})
© www.soinside.com 2019 - 2024. All rights reserved.