axios.ts 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import axios, { AxiosResponse, AxiosRequestConfig, Axios } from 'axios'
  2. import { ResultEnum, ModuleTypeEnum } from "@/enums/httpEnum"
  3. import {PageEnum, ErrorPageNameMap, PreviewEnum} from "@/enums/pageEnum"
  4. import { StorageEnum } from '@/enums/storageEnum'
  5. import { axiosPre } from '@/settings/httpSetting'
  6. import { SystemStoreEnum, SystemStoreUserInfoEnum } from '@/store/modules/systemStore/systemStore.d'
  7. import {
  8. redirectErrorPage,
  9. getLocalStorage,
  10. routerTurnByName,
  11. isPreview,
  12. clearAllSessio,
  13. clearAllStorage,
  14. setLocalStorage,
  15. setSessionStorage, fetchRouteParamsLocation, fetchRouteName, logout, fetchRoutePath
  16. } from '@/utils'
  17. import { fetchAllowList } from './axios.config'
  18. import includes from 'lodash/includes'
  19. import {useSystemStore} from "@/store/modules/systemStore/systemStore";
  20. import { useDialog } from 'naive-ui'
  21. // Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现
  22. // 请求队列
  23. let requestList: any[] = []
  24. // 是否正在刷新中
  25. let isRefreshToken = false
  26. // 请求路径
  27. let base_url=`${import.meta.env.PROD ? import.meta.env.VITE_PRO_PATH : ''}${axiosPre}`
  28. const dialog = useDialog()
  29. export interface MyResponseType<T> {
  30. code: ResultEnum
  31. data: T
  32. message: string
  33. }
  34. export interface MyRequestInstance extends Axios {
  35. <T = any>(config: AxiosRequestConfig): Promise<MyResponseType<T>>
  36. }
  37. const axiosInstance = axios.create({
  38. baseURL: base_url,
  39. timeout: ResultEnum.TIMEOUT,
  40. }) as unknown as MyRequestInstance
  41. axiosInstance.interceptors.request.use(
  42. (config: AxiosRequestConfig) => {
  43. // 获取 tenantId
  44. const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
  45. // console.log(window.location)
  46. const tenantId = info ? info[SystemStoreEnum.TENANT_INFO]['tenantId'] : undefined
  47. if (tenantId) {
  48. config.headers = {
  49. ...config.headers,
  50. 'tenant-id': tenantId
  51. }
  52. }
  53. // 白名单校验
  54. if (includes(fetchAllowList, config.url)) return config
  55. // 获取 token
  56. // 重新登录
  57. if (!info) {
  58. routerTurnByName(PageEnum.BASE_LOGIN_NAME)
  59. return config
  60. }
  61. const userInfo = info[SystemStoreEnum.USER_INFO]
  62. config.headers = {
  63. ...config.headers,
  64. [userInfo[SystemStoreUserInfoEnum.TOKEN_NAME] || 'token']: 'Bearer ' + userInfo[SystemStoreUserInfoEnum.USER_TOKEN] || ''
  65. }
  66. return config
  67. },
  68. (err: AxiosRequestConfig) => {
  69. Promise.reject(err)
  70. }
  71. )
  72. // 响应拦截器
  73. axiosInstance.interceptors.response.use(
  74. async (res: AxiosResponse<any>) => {
  75. const config = res.config
  76. const { code,msg } = res.data as { code: number,msg:string }
  77. if (code === undefined || code === null) return Promise.resolve(res)
  78. // 预览页面错误不进行处理
  79. if (isPreview()&&code!==ResultEnum.TOKEN_OVERDUE) {
  80. return Promise.resolve(res.data)
  81. }
  82. // 如果是验证码的返回,直接返回数据
  83. // 成功
  84. if (code === ResultEnum.SUCCESS) {
  85. return Promise.resolve(res.data)
  86. }
  87. // 登录过期
  88. if (code === ResultEnum.TOKEN_OVERDUE) {
  89. // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了
  90. console.log('Auth2:-------------------准备刷新令牌-----------------')
  91. if (!isRefreshToken) {
  92. isRefreshToken = true
  93. const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
  94. // console.log(window.location)
  95. const refreshToken = info ? info[SystemStoreEnum.USER_INFO][SystemStoreUserInfoEnum.USER_REFRESH_TOKEN] : undefined
  96. // 1. 如果获取不到刷新令牌,则只能执行登出操作
  97. if (!refreshToken) {
  98. console.log('无刷新令牌,即将返回。')
  99. return handleAuthorized()
  100. }
  101. // 2. 进行刷新访问令牌
  102. try {
  103. const systemStore = useSystemStore()
  104. const refreshTokenRes = await getTefreshToken()
  105. // 2.1 刷新成功,则回放队列的请求 + 当前请求
  106. systemStore.setItem(SystemStoreEnum.USER_INFO, {
  107. [SystemStoreUserInfoEnum.USER_TOKEN]: (await refreshTokenRes).data.data.accessToken,
  108. [SystemStoreUserInfoEnum.USER_REFRESH_TOKEN]: (await refreshTokenRes).data.data.refreshToken,
  109. [SystemStoreUserInfoEnum.TOKEN_NAME]: 'Authorization'
  110. })
  111. //修改当前访问令牌
  112. // config.headers!.Authorization = 'Bearer ' + (await refreshTokenRes).data.data.accessToken
  113. const userInfo = info[SystemStoreEnum.USER_INFO]
  114. config.headers = {
  115. ...config.headers,
  116. [userInfo[SystemStoreUserInfoEnum.TOKEN_NAME] || 'token']: 'Bearer ' + userInfo[SystemStoreUserInfoEnum.USER_TOKEN] || ''
  117. }
  118. requestList.forEach((cb: any) => {
  119. cb()
  120. })
  121. requestList = []
  122. console.log('Auth2:-------------------令牌刷新成功-----------------')
  123. return axiosInstance(config)
  124. } catch (e) {
  125. // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。
  126. // 2.2 刷新失败,只回放队列的请求
  127. requestList.forEach((cb: any) => {
  128. cb()
  129. })
  130. // 提示是否要登出。即不回放当前请求!不然会形成递归
  131. return handleAuthorized()
  132. } finally {
  133. requestList = []
  134. isRefreshToken = false
  135. }
  136. } else {
  137. console.log('Auth2:-------------------已添加刷新队列-----------------')
  138. const info = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
  139. // 添加到队列,等待刷新获取到新的令牌
  140. return new Promise((resolve) => {
  141. requestList.push(() => {
  142. const userInfo = info[SystemStoreEnum.USER_INFO]
  143. config.headers!.Authorization = 'Bearer ' + userInfo[SystemStoreUserInfoEnum.USER_TOKEN] // 让每个请求携带自定义token 请根据实际情况自行修改
  144. resolve(axiosInstance(config))
  145. })
  146. })
  147. }
  148. }
  149. // 固定错误码重定向
  150. if (ErrorPageNameMap.get(code)) {
  151. redirectErrorPage(code)
  152. return Promise.resolve(res.data)
  153. }
  154. if (code !== 200) {
  155. if (msg === '无效的刷新令牌') {
  156. // hard coding:忽略这个提示,直接登出
  157. console.log(msg)
  158. return handleAuthorized()
  159. } else {
  160. window['$message'].error(msg)
  161. }
  162. return Promise.reject('error')
  163. }
  164. // 提示错误
  165. window['$message'].error(window['$t']((res.data as any).msg))
  166. return Promise.resolve(res.data)
  167. },
  168. (err: AxiosResponse) => {
  169. Promise.reject(err)
  170. }
  171. )
  172. const getTefreshToken = async () => {
  173. let tenantData = getLocalStorage(StorageEnum.GO_SYSTEM_STORE)
  174. axios.defaults.headers.common['tenant-id'] = tenantData[SystemStoreEnum.TENANT_INFO]['tenantId'] ?? undefined;
  175. return await axios.post(base_url + '/system/auth/refresh-token?refreshToken=' + tenantData[SystemStoreEnum.USER_INFO][SystemStoreUserInfoEnum.USER_REFRESH_TOKEN] ?? undefined)
  176. }
  177. const handleAuthorized = () =>{
  178. // window['$message'].error(window['$t']('http.token_overdue_message'))
  179. console.log(useDialog())
  180. window['$dialog'].warning( {title: '登录已超时',
  181. content: '登录超时,请重新登录。',
  182. positiveText: '确定',
  183. negativeText: '取消',
  184. onPositiveClick: () => {
  185. // session 不需要清除 clearAllSessio() //清除所有Session缓存
  186. clearAllStorage() //清除所有Storage缓存
  187. //临时缓存跳回目录进行登录重新跳回
  188. if('/chart/preview' === fetchRoutePath()){
  189. setSessionStorage('setRedirectPath','/chart/preview')
  190. setSessionStorage('setRedirectPathId', fetchRouteParamsLocation())
  191. }
  192. logout()
  193. return Promise.resolve(window['$t']('http.token_overdue_message'))
  194. }
  195. })
  196. }
  197. export default axiosInstance