增加租户切换功能
This commit is contained in:
1 parent
1685b22e1e
commit
0dc5c2f075
8 files changed
+305
-60
No files matched your search
@@ -164,8 +164,22 @@ export async function handleAuthorizeCallback(query = {}) {
|
||||
return data;
|
||||
}
|
||||
|
||||
/** 静默刷新访问令牌 */
|
||||
// 刷新中的令牌请求(single-flight):并发 401 时共用同一次刷新。
|
||||
// 认证中心的 refresh_token 是一次性轮换的,同一个 refresh_token 被使用两次会被
|
||||
// 判定为重放攻击并吊销整个会话(见 services/auth/token.go 的 RefreshTokens),
|
||||
// 因此这里必须保证「任一时刻只有一个刷新请求在飞」。
|
||||
let refreshingPromise = null;
|
||||
|
||||
/** 静默刷新访问令牌(带并发去重) */
|
||||
export async function refreshAccessToken() {
|
||||
if (refreshingPromise) return refreshingPromise;
|
||||
refreshingPromise = doRefreshAccessToken().finally(() => {
|
||||
refreshingPromise = null;
|
||||
});
|
||||
return refreshingPromise;
|
||||
}
|
||||
|
||||
async function doRefreshAccessToken() {
|
||||
const refresh = getRefreshToken();
|
||||
if (!refresh) throw new Error("无刷新令牌");
|
||||
|
||||
@@ -180,7 +194,9 @@ export async function refreshAccessToken() {
|
||||
body,
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok || !data.access_token) throw new Error("刷新令牌失败");
|
||||
if (!res.ok || !data.access_token) {
|
||||
throw new Error(data.error_description || data.error || "刷新令牌失败");
|
||||
}
|
||||
setTokens(data);
|
||||
return data.access_token;
|
||||
}
|
||||
@@ -266,9 +282,21 @@ export async function ensureUserInfo() {
|
||||
|
||||
/** 拉取当前登录用户(认证中心视角) */
|
||||
export async function fetchUserInfo() {
|
||||
const res = await fetch(`${AUTH_BASE}/userinfo`, {
|
||||
let res = await fetch(`${AUTH_BASE}/userinfo`, {
|
||||
headers: { Authorization: `Bearer ${getAccessToken()}` },
|
||||
});
|
||||
if (res.status === 401) {
|
||||
// 访问令牌可能刚过期:先静默续期再重试一次,
|
||||
// 否则调用方(路由守卫的 ensureUserInfo)会把用户直接踢回认证中心登录
|
||||
try {
|
||||
const newToken = await refreshAccessToken();
|
||||
res = await fetch(`${AUTH_BASE}/userinfo`, {
|
||||
headers: { Authorization: `Bearer ${newToken}` },
|
||||
});
|
||||
} catch (e) {
|
||||
// 续期失败:保持原响应,交给调用方按「未登录」处理
|
||||
}
|
||||
}
|
||||
if (!res.ok) throw new Error("获取用户信息失败");
|
||||
return res.json();
|
||||
}
|
||||
@@ -284,7 +312,17 @@ export async function switchTenant(tid) {
|
||||
body: JSON.stringify({ tid, client_id: CLIENT_ID }),
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok || data.code !== 200) throw new Error(data.msg || "切换企业失败");
|
||||
if (!res.ok || data.code !== 200) {
|
||||
const msg = data.msg || "切换企业失败";
|
||||
// 会话被吊销/过期:本端已无法继续,清掉登录态回认证中心重新登录,
|
||||
// 否则用户会卡在「点了没反应」的状态
|
||||
if (res.status === 401 || msg.includes("会话") || msg.includes("重新登录")) {
|
||||
clearTokens();
|
||||
localStorage.removeItem("userInfo");
|
||||
redirectToAuthorize().catch((err) => console.error("跳转统一认证失败:", err));
|
||||
}
|
||||
throw new Error(msg);
|
||||
}
|
||||
if (data.data?.tokens) setTokens(data.data.tokens);
|
||||
// 切换后同步本地缓存的企业信息,避免顶部仍显示切换前的企业名
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user