axios.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /**
  2. * 全站http配置
  3. *
  4. * axios参数说明
  5. * isSerialize是否开启form表单提交
  6. * isToken是否需要token
  7. */
  8. import axios from 'axios';
  9. import store from '@/store/';
  10. import router from '@/router/router';
  11. import {serialize} from '@/util/util';
  12. import {getToken, removeToken, removeRefreshToken} from '@/util/auth';
  13. import {Message} from 'element-ui';
  14. import {isURL, validatenull} from "@/util/validate";
  15. import website from '@/config/website';
  16. import {Base64} from 'js-base64';
  17. import { baseUrl } from '@/config/env';
  18. import NProgress from 'nprogress';
  19. import 'nprogress/nprogress.css';
  20. import crypto from '@/util/crypto';
  21. // 全局未授权错误提示状态,只提示一次
  22. let isErrorShown = false;
  23. //默认超时时间
  24. axios.defaults.timeout = 30 * 1000 * 60; // 设置超时时间为30分钟
  25. //返回其他状态码
  26. axios.defaults.validateStatus = function (status) {
  27. return status >= 200 && status <= 500;
  28. };
  29. //跨域请求,允许保存cookie
  30. axios.defaults.withCredentials = true;
  31. // NProgress 配置
  32. NProgress.configure({
  33. showSpinner: false
  34. });
  35. // 全局锁机制:刷新token相关的状态
  36. let isRefreshing = false; // 是否正在刷新token
  37. let refreshTokenPromise = null; // 刷新token的Promise,用于等待刷新完成后执行其他请求
  38. //http request拦截
  39. axios.interceptors.request.use(config => {
  40. //开启 progress bar
  41. NProgress.start();
  42. // 初始化错误提示状态
  43. isErrorShown = false;
  44. //地址为已经配置状态则不添加前缀
  45. if (!isURL(config.url) && !config.url.startsWith(baseUrl)) {
  46. config.url = baseUrl + config.url;
  47. }
  48. //安全请求header
  49. config.headers['Kd-Requested-With'] = 'KdHttpRequest';
  50. //headers判断是否需要
  51. const authorization = config.authorization === false;
  52. if (!authorization) {
  53. config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;
  54. }
  55. //headers判断请求是否携带token
  56. const meta = (config.meta || {});
  57. const isToken = meta.isToken === false;
  58. //headers传递token是否加密
  59. const cryptoToken = config.cryptoToken === true;
  60. //判断传递数据是否加密
  61. const cryptoData = config.cryptoData === true;
  62. const token = getToken();
  63. if (token && !isToken) {
  64. config.headers[website.tokenHeader] = cryptoToken
  65. ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
  66. : 'bearer ' + token;
  67. }
  68. // 开启报文加密
  69. if (cryptoData) {
  70. if (config.params) {
  71. const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey);
  72. config.params = { data };
  73. }
  74. if (config.data) {
  75. config.text = true;
  76. config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey);
  77. }
  78. }
  79. //headers中配置text请求
  80. if (config.text === true) {
  81. config.headers["Content-Type"] = "text/plain";
  82. }
  83. //headers中配置serialize为true开启序列化
  84. if (config.method === 'post' && meta.isSerialize === true) {
  85. config.data = serialize(config.data);
  86. }
  87. return config;
  88. }, error => {
  89. return Promise.reject(error);
  90. });
  91. //http response拦截
  92. axios.interceptors.response.use(res => {
  93. //关闭 progress bar
  94. NProgress.done();
  95. //获取配置信息
  96. const config = res.config;
  97. const cryptoData = config.cryptoData === true;
  98. //解析加密报文
  99. if (cryptoData) {
  100. res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey));
  101. }
  102. //获取状态信息
  103. const status = res.data.error_code || res.data.code || res.status;
  104. const statusWhiteList = website.statusWhiteList || [];
  105. const message = res.data.msg || res.data.error_description || '系统错误';
  106. //如果在白名单里则自行catch逻辑处理
  107. if (statusWhiteList.includes(status)) return Promise.reject(res);
  108. // 如果是401并且没有重试过,尝试刷新token
  109. if (status === 401 && !config._retry) {
  110. // 标记此请求已尝试刷新token
  111. config._retry = true;
  112. // 如果已经在刷新token,等待刷新完成再执行请求
  113. if (isRefreshing) {
  114. return refreshTokenPromise.then(() => {
  115. const meta = config.meta || {};
  116. const isToken = meta.isToken === false;
  117. const cryptoToken = config.cryptoToken === true;
  118. const token = getToken();
  119. if (token && !isToken) {
  120. config.headers[website.tokenHeader] = cryptoToken
  121. ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
  122. : 'bearer ' + token;
  123. }
  124. return axios(config);
  125. });
  126. }
  127. // 标记正在刷新token
  128. isRefreshing = true;
  129. // 调用RefreshToken action来刷新token
  130. refreshTokenPromise = store.dispatch('RefreshToken')
  131. .then(() => {
  132. isRefreshing = false;
  133. const meta = config.meta || {};
  134. const isToken = meta.isToken === false;
  135. const cryptoToken = config.cryptoToken === true;
  136. // 获取刷新后的token
  137. const token = getToken();
  138. if (token && !isToken) {
  139. config.headers[website.tokenHeader] = cryptoToken
  140. ? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey)
  141. : 'bearer ' + token;
  142. }
  143. return axios(config);
  144. })
  145. .catch(() => {
  146. isRefreshing = false;
  147. // 首次报错时提示
  148. if (!isErrorShown) {
  149. isErrorShown = true;
  150. Message({
  151. message: '用户令牌过期,请重新登录',
  152. type: 'error',
  153. });
  154. }
  155. // 清除token信息
  156. removeToken();
  157. removeRefreshToken();
  158. // 重定向到登录页
  159. store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }));
  160. return Promise.reject(new Error(message));
  161. });
  162. return refreshTokenPromise;
  163. }
  164. // 如果是401并且已经重试过,直接跳转到登录页面
  165. if (status === 401 && config._retry) {
  166. // 首次报错时提示
  167. if (!isErrorShown) {
  168. isErrorShown = true;
  169. Message({
  170. message: '用户令牌过期,请重新登录',
  171. type: 'error',
  172. });
  173. }
  174. // 清除token信息
  175. removeToken();
  176. removeRefreshToken();
  177. // 重定向到登录页
  178. store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }));
  179. return Promise.reject(new Error(message));
  180. }
  181. // 如果请求为oauth2错误码则首次报错时提示
  182. if (status > 2000 && !validatenull(res.data.error_description)) {
  183. // 首次报错时提示
  184. if (!isErrorShown) {
  185. isErrorShown = true;
  186. Message({
  187. message: message,
  188. type: 'error',
  189. });
  190. }
  191. return Promise.reject(new Error(message));
  192. }
  193. // 如果请求为非200否者默认统一处理
  194. if (status !== 200) {
  195. Message({
  196. message: message,
  197. type: 'error'
  198. });
  199. return Promise.reject(new Error(message));
  200. }
  201. return res;
  202. }, error => {
  203. NProgress.done();
  204. return Promise.reject(new Error(error));
  205. });
  206. export default axios;