|
@@ -8,12 +8,26 @@
|
|
|
import axios from 'axios';
|
|
import axios from 'axios';
|
|
|
import JSONBIG from 'json-bigint';
|
|
import JSONBIG from 'json-bigint';
|
|
|
import { MessageBox } from 'element-ui';
|
|
import { MessageBox } from 'element-ui';
|
|
|
-import { API_BASE_URL, TOKEN_HEADER_NAME } from '@/config/setting';
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ API_BASE_URL,
|
|
|
|
|
+ TOKEN_HEADER_NAME,
|
|
|
|
|
+ TENANT_HEADER_NAME,
|
|
|
|
|
+} from '@/config/setting';
|
|
|
import { getRouter, resetRouter } from '@/router';
|
|
import { getRouter, resetRouter } from '@/router';
|
|
|
import { getToken, refreshToken, removeToken } from '@/utils/token-util';
|
|
import { getToken, refreshToken, removeToken } from '@/utils/token-util';
|
|
|
|
|
+import { getTenantId } from '@/utils/tenant-util';
|
|
|
|
|
|
|
|
let loginExpiredDialogVisible = false;
|
|
let loginExpiredDialogVisible = false;
|
|
|
|
|
|
|
|
|
|
+/**
|
|
|
|
|
+ * 是否为签发登录凭证的入口(对应后端 TenantIsolationAspect 的 LOGIN_URIS)。
|
|
|
|
|
+ * @param {string} url 请求地址
|
|
|
|
|
+ * @returns {boolean} 是登录入口返回 true
|
|
|
|
|
+ */
|
|
|
|
|
+function isLoginEntry(url) {
|
|
|
|
|
+ return typeof url === 'string' && /(^|\/)user\/login(\?|$)/.test(url);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/** 跳转登录页,独立运行和 qiankun 嵌入运行分别处理。 */
|
|
/** 跳转登录页,独立运行和 qiankun 嵌入运行分别处理。 */
|
|
|
function redirectToLogin(from) {
|
|
function redirectToLogin(from) {
|
|
|
const isHome =
|
|
const isHome =
|
|
@@ -103,11 +117,24 @@ const request = axios.create({
|
|
|
|
|
|
|
|
request.interceptors.request.use(
|
|
request.interceptors.request.use(
|
|
|
(config) => {
|
|
(config) => {
|
|
|
- const token = getToken();
|
|
|
|
|
|
|
+ // 登录入口必须以匿名身份发出:残留的旧凭证在服务端优先级高于请求头
|
|
|
|
|
+ // (token 的租户声明才是权威),它既会顶掉本次选择的租户,又会让 X-Tenant-ID
|
|
|
|
|
+ // 被当成外部伪造而忽略 —— 表现为"选了租户却登进平台库"。
|
|
|
|
|
+ const token = isLoginEntry(config.url) ? '' : getToken();
|
|
|
if (token) {
|
|
if (token) {
|
|
|
config.headers = config.headers || {};
|
|
config.headers = config.headers || {};
|
|
|
config.headers[TOKEN_HEADER_NAME] = token;
|
|
config.headers[TOKEN_HEADER_NAME] = token;
|
|
|
}
|
|
}
|
|
|
|
|
+ // 多租户:未登录的请求(登录接口、登录页品牌标识、扫码入职等匿名入口)租户只能靠
|
|
|
|
|
+ // 请求头传递 —— 服务端必须在认证之前就知道连哪个租户库。
|
|
|
|
|
+ // 已登录时租户已写入 token 声明、服务端以声明为准,这里不重复携带,
|
|
|
|
|
+ // 避免为"多余且会被忽略"的头部逐个打告警日志。
|
|
|
|
|
+ if (!token && config.headers) {
|
|
|
|
|
+ const tenantId = getTenantId();
|
|
|
|
|
+ if (tenantId) {
|
|
|
|
|
+ config.headers[TENANT_HEADER_NAME] = tenantId;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
return config;
|
|
return config;
|
|
|
},
|
|
},
|
|
|
(error) => Promise.reject(error),
|
|
(error) => Promise.reject(error),
|