CSRF Token 刷新后前端怎么同步更新?

Top丶艺霖 阅读 48

我在用 Axios 拦截器处理 CSRF Token 过期的情况,后端返回 419 时会刷新 token 并在响应头里带新的 XSRF-TOKEN。但我不确定怎么安全地把新 token 应用到后续请求里,现在每次刷新页面才生效,体验很差。

我试过在拦截器里直接更新 axios.defaults.headers.common[‘X-XSRF-TOKEN’],但好像没起作用,或者有竞态问题?

axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response?.status === 419) {
      const newToken = error.response.headers['xsrf-token'];
      if (newToken) {
        axios.defaults.headers.common['X-XSRF-TOKEN'] = newToken;
        // 这里要不要重新发起原请求?怎么保证其他并发请求也用新 token?
      }
    }
    return Promise.reject(error);
  }
);
我来解答 赞 5 收藏
二维码
手机扫码查看
2 条解答
一羽墨
一羽墨 Lv1
这个问题我在项目里踩过坑,按照规范确实要注意几个关键点。

首先你需要在拦截器里做两件事:
1. 更新axios的默认headers
2. 更新cookie里的XSRF-TOKEN(因为axios默认是从cookie读取的)

关键是要用document.cookie同步更新cookie,不然下次请求axios还是会用旧的。然后重新发起原请求也很重要,不然用户得手动刷新页面。

改进后的代码大概这样:

axios.interceptors.response.use(
response => response,
async error => {
if (error.response?.status === 419) {
const newToken = error.response.headers['xsrf-token'];
if (newToken) {
// 更新axios配置
axios.defaults.headers.common['X-XSRF-TOKEN'] = newToken;
// 更新cookie
document.cookie = XSRF-TOKEN=${newToken}; path=/;
// 重新发起原请求
return axios(error.config);
}
}
return Promise.reject(error);
}
);


关于竞态问题,这样处理就够了。因为axios内部会用最新的headers,其他并发请求会自动带上新token。我实测过没问题的,除非你项目里有特别奇葩的封装。

ps. 记得让后端设置Access-Control-Expose-Headersxsrf-token暴露出来,不然前端拿不到响应头。
点赞
2026-03-07 11:00
Mr.钰莹
Mr.钰莹 Lv1
别直接改 defaults,那玩意儿管不到已经发出去的请求。你需要加个锁机制防止并发重复刷新,拿到新 token 后更新请求头并重试原请求。改成这样:

let isRefreshing = false;
let subscribers = [];

axios.interceptors.response.use(response => response, error => {
const originalRequest = error.config;
// 只有 419 错误且没标记过重试时才处理
if (error.response?.status === 419 && !originalRequest._retry) {
if (isRefreshing) {
// 如果正在刷新,把请求加入队列等待
return new Promise(resolve => {
subscribers.push(token => {
originalRequest.headers['X-XSRF-TOKEN'] = token;
resolve(axios(originalRequest));
});
});
}

originalRequest._retry = true;
isRefreshing = true;

const newToken = error.response.headers['xsrf-token'];

// 更新 Cookie 或本地存储,这里假设后端已经处理了
// 触发队列里的请求
subscribers.forEach(cb => cb(newToken));
subscribers = [];
isRefreshing = false;

// 更新当前请求头并重试
originalRequest.headers['X-XSRF-TOKEN'] = newToken;
return axios(originalRequest);
}
return Promise.reject(error);
});
点赞 3
2026-03-03 21:36