使用过期令牌发出同时 API 请求时如何避免多个令牌刷新请求

Posted

技术标签:

【中文标题】使用过期令牌发出同时 API 请求时如何避免多个令牌刷新请求【英文标题】:How to avoid multiple token refresh requests when making simultaneous API requests with an expired token 【发布时间】:2020-06-30 03:40:54 【问题描述】:

使用 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) 验证到期日期。

对不起,英语不好...

【问题讨论】:

【参考方案1】:

您需要做的是在拦截器之外维护一些状态。说什么

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

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

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)
)

【讨论】:

我在一个 React 项目中使用过(稍作修改)并且工作得也很好。谢谢! @FernandoBarbosa 你能把你的代码分享给我吗?!

以上是关于使用过期令牌发出同时 API 请求时如何避免多个令牌刷新请求的主要内容,如果未能解决你的问题,请参考以下文章

向服务器发送多个请求时,Okhttp 刷新过期令牌

重新发送使用过期令牌发出的请求会导致开发人员工具处于待处理状态

使用多个请求刷新访问令牌

如果令牌在服务之间过期怎么办?

Spring Boot 安全性 - 允许使用过期 JWT 令牌的用户请求

在发出克隆请求之前更新令牌