interceptor.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import axios from 'axios';
  2. import type {
  3. InternalAxiosRequestConfig,
  4. AxiosResponse,
  5. AxiosRequestHeaders,
  6. } from 'axios';
  7. import router from '@/router';
  8. import { Message, Modal } from '@arco-design/web-vue';
  9. import { useUserStore } from '@/store';
  10. import { useAuth } from '@/utils/auth';
  11. import { refreshToken } from '@/api/user';
  12. const { getToken, getRefreshToken, setToken, clearToken } = useAuth();
  13. let isRefreshing = false;
  14. let subscribers: ((token: string) => void)[] = [];
  15. function subscribeTokenRefresh(cb: (token: string) => void) {
  16. subscribers.push(cb);
  17. }
  18. function onRefreshed(token: string) {
  19. subscribers.forEach(cb => cb(token));
  20. subscribers = [];
  21. }
  22. const instance = axios.create({
  23. baseURL: '/',
  24. });
  25. // 新增一个独立的函数来处理 Token 刷新
  26. async function handleTokenRefresh(
  27. refreshTokenValue: string,
  28. originalConfig: InternalAxiosRequestConfig
  29. ) {
  30. isRefreshing = true;
  31. try {
  32. const res = await refreshToken({ token: refreshTokenValue });
  33. if (res && res.data && res.success) {
  34. const { token: newAccessToken, refreshToken: newRefreshToken } = res.data;
  35. setToken(newAccessToken, newRefreshToken);
  36. onRefreshed(newAccessToken);
  37. // 重试原始请求
  38. originalConfig.headers.Authorization = `${newAccessToken}`;
  39. return instance(originalConfig);
  40. } else {
  41. isRefreshing = false;
  42. clearToken();
  43. router.push({ name: 'login' });
  44. throw new Error('Invalid token refresh response');
  45. }
  46. } catch (error) {
  47. Message.error('登录状态已过期,请重新登录');
  48. clearToken();
  49. router.push({ name: 'login' });
  50. throw error; // 重新抛出错误,让外部拦截器可以继续处理
  51. } finally {
  52. isRefreshing = false;
  53. }
  54. }
  55. instance.interceptors.request.use(
  56. (config: InternalAxiosRequestConfig) => {
  57. // let each request carry token
  58. // this example using the JWT token
  59. // Authorization is a custom headers key
  60. // please modify it according to the actual situation
  61. const token = getToken();
  62. if (token) {
  63. if (!config.headers) {
  64. config.headers = {} as AxiosRequestHeaders;
  65. }
  66. config.headers.Authorization = `${token}`;
  67. }
  68. return config;
  69. },
  70. error => {
  71. // do something
  72. return Promise.reject(error);
  73. }
  74. );
  75. // 响应拦截器修复版本
  76. instance.interceptors.response.use(
  77. (response: AxiosResponse) => {
  78. return response;
  79. },
  80. async error => {
  81. const { config, response } = error;
  82. const refreshTokenValue = getRefreshToken();
  83. if (!response) {
  84. // 网络错误处理
  85. Message.error('网络连接异常');
  86. return Promise.reject(error);
  87. }
  88. if (response.status === 401) {
  89. if (!isRefreshing) {
  90. isRefreshing = true;
  91. config._retry = true;
  92. if (refreshTokenValue) {
  93. return handleTokenRefresh(refreshTokenValue, config);
  94. }
  95. } else {
  96. return new Promise(resolve => {
  97. subscribeTokenRefresh((token: string) => {
  98. config.headers.Authorization = `${token}`;
  99. resolve(axios(config));
  100. });
  101. });
  102. }
  103. } else if (response.status === 403) {
  104. Message.error('没有权限执行此操作');
  105. // 可选:跳转到权限不足页面或首页
  106. // router.push({ name: 'forbidden' });
  107. } else if (response.status === 404) {
  108. Message.error('请求的资源不存在');
  109. } else if (response.status >= 500) {
  110. Message.error('服务器内部错误,请稍后再试');
  111. } else {
  112. Message.error(
  113. error.response?.data?.message || `请求错误: ${response.status}`
  114. );
  115. }
  116. return Promise.reject(error);
  117. }
  118. );
  119. export default instance;