Axios拦截器实例(token)

request.js

  1. import axios from "axios";
  2. import Vue from "vue";
  3. import { parseToken, getRefreshToken } from "./token";
  4. // axios 配置
  5. // axios.defaults.timeout = 8000;
  6. const service = axios.create({
  7. baseURL: "https://api.job.sunxinao.cn/", // url = base url + request url
  8. // withCredentials: true, // send cookies when cross-domain requests
  9. timeout: 5000 // request timeout
  10. });
  11. // Loading 实例
  12. let loading;
  13. function startLoading() {
  14. loading = Vue.prototype.$loading({
  15. lock: true,
  16. text: "加载中...",
  17. });
  18. }
  19. function endLoading() {
  20. setTimeout(() => {
  21. loading.close();
  22. }, 300);
  23. }
  24. let needLoadingRequestCount = 0;
  25. function showFullScreenLoading() {
  26. if (needLoadingRequestCount === 0) {
  27. startLoading();
  28. }
  29. needLoadingRequestCount++;
  30. }
  31. function tryHideFullScreenLoading() {
  32. if (needLoadingRequestCount <= 0) return;
  33. needLoadingRequestCount--;
  34. if (needLoadingRequestCount === 0) {
  35. endLoading();
  36. }
  37. }
  38. service.interceptors.request.use(
  39. config => {
  40. //当请求的api不是检查用户是否存在时,启动加载动画
  41. if (config.url !== "/reg/valid/suppress_xhr_error") {
  42. }
  43. showFullScreenLoading();
  44. let bearerToken = localStorage.getItem("bearer_token") || "";
  45. let refreshToken = localStorage.getItem("refresh_token") || "";
  46. if (config.headers["authorization"] === undefined) {
  47. if (config.url.endsWith("/refresh_token")) {
  48. config.headers["authorization"] = "Bearer " + refreshToken;
  49. } else if (bearerToken.length !== 0) {
  50. config.headers["authorization"] = bearerToken;
  51. }
  52. }
  53. return config;
  54. },
  55. error => {
  56. tryHideFullScreenLoading();
  57. return Promise.reject(error);
  58. }
  59. );
  60. // 添加响应拦截器
  61. service.interceptors.response.use(
  62. response => {
  63. tryHideFullScreenLoading();
  64. if (response.headers["authorization"]) {
  65. localStorage.setItem("bearer_token", response.headers["authorization"]);
  66. let token = response.headers["authorization"].replace(/Bearer\s*/i, "");
  67. let jwt = parseToken(token);
  68. let userId = jwt["payload"]["sub"];
  69. localStorage.setItem("user_id", userId);
  70. }
  71. return response;
  72. },
  73. async error => {
  74. tryHideFullScreenLoading();
  75. let response = error.response;
  76. if (response && response.status === 401) {
  77. // 当前会话已失效,删除 token 以进入匿名状态
  78. localStorage.removeItem("bearer_token");
  79. localStorage.removeItem("user_id");
  80. if (response.data && response.data["error"] === "invalid_token") {
  81. let refreshToken = localStorage.getItem("refresh_token") || "";
  82. // 尝试刷新 token
  83. if (refreshToken.length !== 0) {
  84. let refreshToken = await getRefreshToken();
  85. localStorage.setItem("refresh_token", refreshToken);
  86. if (refreshToken.length !== 0) {
  87. // FIXME 刷新成功的话重新发请求 (待测试)
  88. try {
  89. delete response.config.headers["authorization"];
  90. return await axios(response.config);
  91. } catch (e) {
  92. //会话刷新失败
  93. this.$confirm(
  94. "未登录或会话失效,您可以取消停留在此页面,或重新登录",
  95. "提示", {
  96. confirmButtonText: "确定",
  97. cancelButtonText: "取消",
  98. type: "warning"
  99. }
  100. )
  101. .then(() => {
  102. this.$router.push("/login");
  103. })
  104. .catch(() => {
  105. location.reload();
  106. });
  107. return Promise.reject(e);
  108. }
  109. } else {
  110. //未登录
  111. this.$confirm(
  112. "未登录或会话失效,您可以取消停留在此页面,或重新登录",
  113. "提示", {
  114. confirmButtonText: "确定",
  115. cancelButtonText: "取消",
  116. type: "warning"
  117. }
  118. )
  119. .then(() => {
  120. this.$router.push("/login");
  121. })
  122. .catch(() => {
  123. location.reload();
  124. });
  125. }
  126. }
  127. }
  128. }
  129. Vue.prototype.$message({
  130. message: error.message,
  131. type: "error",
  132. duration: 5 * 1000
  133. });
  134. return Promise.reject(error);
  135. }
  136. );
  137. export default service;

token.js

  1. import request from "./request";
  2. import base64 from "base64-js";
  3. /**
  4. * 解析 token
  5. *
  6. * @param {string} token JWT 字符串
  7. * @returns {{payload: any, header: any}} 解析后的对象
  8. */
  9. export function parseToken(token) {
  10. let decoder = new TextDecoder();
  11. let tokenHeader = token
  12. .split(".")[0]
  13. .replace(/-/gi, "+")
  14. .replace(/_/gi, "/");
  15. let tokenPayload = token
  16. .split(".")[1]
  17. .replace(/-/gi, "+")
  18. .replace(/_/gi, "/");
  19. while (tokenPayload.length % 4 !== 0) tokenPayload += "=";
  20. while (tokenHeader.length % 4 !== 0) tokenHeader += "=";
  21. let jsonStr = decoder.decode(base64.toByteArray(tokenPayload));
  22. let payload = JSON.parse(jsonStr);
  23. jsonStr = decoder.decode(base64.toByteArray(tokenHeader));
  24. let header = JSON.parse(jsonStr);
  25. return {
  26. header: header,
  27. payload: payload
  28. };
  29. }
  30. /**
  31. * 刷新令牌
  32. * @return {Promise<string>} 返回的 Refresh Token
  33. */
  34. export async function getRefreshToken() {
  35. return await request
  36. .get("/oauth/refresh_token")
  37. .then(result => {
  38. let data = result.data;
  39. let refreshToken = data["refresh_token"];
  40. console.log("token 刷新成功: " + data["access_token"]);
  41. return refreshToken;
  42. })
  43. .catch(() => "");
  44. }