fix(auth): normalize external accessToken and preserve on empty yUser login
This commit is contained in:
1 parent
44d7304f8e
commit
1847eb9d6a
5 files changed
+53
-20
No files matched your search
@@ -0,0 +1,7 @@
|
|||||||
|
export function normalizeExternalAccessToken(value: unknown): string {
|
||||||
|
if (value == null) return "";
|
||||||
|
const token = String(value).trim();
|
||||||
|
if (!token) return "";
|
||||||
|
const lowered = token.toLowerCase();
|
||||||
|
return lowered === "null" || lowered === "undefined" ? "" : token;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { requestCrudService } from "@/service/request-axios-service";
|
import { requestCrudService } from "@/service/request-axios-service";
|
||||||
import { authStorageGet, authStorageSet } from "@/core/auth/scoped-storage";
|
import { authStorageGet, authStorageSet } from "@/core/auth/scoped-storage";
|
||||||
|
import { normalizeExternalAccessToken } from "@/core/auth/external-access-token";
|
||||||
|
|
||||||
const CONSUMER_URL = import.meta.env.VITE_AIPI_CONSUMER_URL || "";
|
const CONSUMER_URL = import.meta.env.VITE_AIPI_CONSUMER_URL || "";
|
||||||
|
|
||||||
@@ -15,6 +16,24 @@ type UserDetailResponse = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function readExternalUserInfo(): Record<string, unknown> {
|
||||||
|
try {
|
||||||
|
const raw = authStorageGet(EXTERNAL_USER_INFO_STORAGE_KEY);
|
||||||
|
if (!raw) return {};
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
||||||
|
? (parsed as Record<string, unknown>)
|
||||||
|
: {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readStoredYUserId(info: Record<string, unknown>): string {
|
||||||
|
const value = info.yUserId;
|
||||||
|
return value == null ? "" : String(value).trim();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* External login via yUserId + optional token (same flow as web-code Login.tsx):
|
* External login via yUserId + optional token (same flow as web-code Login.tsx):
|
||||||
* persist tools_token, resolve user via consumer API, store userId/userInfo.
|
* persist tools_token, resolve user via consumer API, store userId/userInfo.
|
||||||
@@ -38,10 +57,23 @@ export async function loginByYUserId(
|
|||||||
// 注意:接口也会返回 accessToken,但 XK/PG 及 source=accessToken 要用路由透传的
|
// 注意:接口也会返回 accessToken,但 XK/PG 及 source=accessToken 要用路由透传的
|
||||||
// access_token,故登录流程随后会用 persistAccessToken 覆盖 userInfo.accessToken。
|
// access_token,故登录流程随后会用 persistAccessToken 覆盖 userInfo.accessToken。
|
||||||
const result = res.data ?? {};
|
const result = res.data ?? {};
|
||||||
authStorageSet(
|
const previous = readExternalUserInfo();
|
||||||
EXTERNAL_USER_INFO_STORAGE_KEY,
|
const next: Record<string, unknown> = { ...result, yUserId };
|
||||||
JSON.stringify({ ...result, yUserId }),
|
const incomingAccessToken = normalizeExternalAccessToken(result.accessToken);
|
||||||
);
|
if (incomingAccessToken) {
|
||||||
|
next.accessToken = incomingAccessToken;
|
||||||
|
} else {
|
||||||
|
const previousAccessToken = normalizeExternalAccessToken(previous.accessToken);
|
||||||
|
const previousYUserId = readStoredYUserId(previous);
|
||||||
|
// Do not let an unstable upstream empty/null token wipe a usable token for
|
||||||
|
// the same external user. Avoid carrying it across different yUserId values.
|
||||||
|
if (previousAccessToken && previousYUserId === yUserId) {
|
||||||
|
next.accessToken = previousAccessToken;
|
||||||
|
} else {
|
||||||
|
delete next.accessToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
authStorageSet(EXTERNAL_USER_INFO_STORAGE_KEY, JSON.stringify(next));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function persistToolsToken(token: string): void {
|
export function persistToolsToken(token: string): void {
|
||||||
@@ -56,20 +88,9 @@ export function persistToolsToken(token: string): void {
|
|||||||
* 用合并而非覆盖整对象,避免抹掉 loginByYUserId 从 consumer 拿到的用户信息。
|
* 用合并而非覆盖整对象,避免抹掉 loginByYUserId 从 consumer 拿到的用户信息。
|
||||||
*/
|
*/
|
||||||
export function persistAccessToken(token: string): void {
|
export function persistAccessToken(token: string): void {
|
||||||
const trimmed = token?.trim();
|
const trimmed = normalizeExternalAccessToken(token);
|
||||||
if (!trimmed) return;
|
if (!trimmed) return;
|
||||||
let info: Record<string, unknown> = {};
|
const info = readExternalUserInfo();
|
||||||
try {
|
|
||||||
const raw = authStorageGet(EXTERNAL_USER_INFO_STORAGE_KEY);
|
|
||||||
if (raw) {
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
if (parsed && typeof parsed === "object") {
|
|
||||||
info = parsed as Record<string, unknown>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
info = {};
|
|
||||||
}
|
|
||||||
info.accessToken = trimmed;
|
info.accessToken = trimmed;
|
||||||
authStorageSet(EXTERNAL_USER_INFO_STORAGE_KEY, JSON.stringify(info));
|
authStorageSet(EXTERNAL_USER_INFO_STORAGE_KEY, JSON.stringify(info));
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/** Strategy 模块外部 iframe / 嵌入页链接(来自 Vite 环境变量) */
|
/** Strategy 模块外部 iframe / 嵌入页链接(来自 Vite 环境变量) */
|
||||||
|
|
||||||
import { authStorageGet } from "@/core/auth/scoped-storage";
|
import { authStorageGet } from "@/core/auth/scoped-storage";
|
||||||
|
import { normalizeExternalAccessToken } from "@/core/auth/external-access-token";
|
||||||
|
|
||||||
function trimTrailingSlash(url: string): string {
|
function trimTrailingSlash(url: string): string {
|
||||||
return url.replace(/\/+$/, "");
|
return url.replace(/\/+$/, "");
|
||||||
@@ -43,8 +44,7 @@ function readUserInfo(): Record<string, unknown> {
|
|||||||
|
|
||||||
/** 当前登录态 accessToken(无则空串)。 */
|
/** 当前登录态 accessToken(无则空串)。 */
|
||||||
export function getAccessToken(): string {
|
export function getAccessToken(): string {
|
||||||
const value = readUserInfo().accessToken;
|
return normalizeExternalAccessToken(readUserInfo().accessToken);
|
||||||
return value == null ? "" : String(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@ import {
|
|||||||
getKnowledgeIframeSkin,
|
getKnowledgeIframeSkin,
|
||||||
} from "@/core/strategy/external-links";
|
} from "@/core/strategy/external-links";
|
||||||
import { authStorageGet } from "@/core/auth/scoped-storage";
|
import { authStorageGet } from "@/core/auth/scoped-storage";
|
||||||
|
import { normalizeExternalAccessToken } from "@/core/auth/external-access-token";
|
||||||
import { requestCrudService } from "@/service/request-axios-service";
|
import { requestCrudService } from "@/service/request-axios-service";
|
||||||
import ExternalIframePage from "../common/ExternalIframePage";
|
import ExternalIframePage from "../common/ExternalIframePage";
|
||||||
|
|
||||||
@@ -24,7 +25,7 @@ const KnowledgeBaseResourceManagement: React.FC = () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const userInfo = JSON.parse(jsonStr);
|
const userInfo = JSON.parse(jsonStr);
|
||||||
const token = userInfo.accessToken;
|
const token = normalizeExternalAccessToken(userInfo?.accessToken);
|
||||||
if (!token) return;
|
if (!token) return;
|
||||||
|
|
||||||
const res = (await service.post(apiUrl, {
|
const res = (await service.post(apiUrl, {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import type { LightApp, LightAppRouteParam } from "@/strategy-components/api/light-apps";
|
import type { LightApp, LightAppRouteParam } from "@/strategy-components/api/light-apps";
|
||||||
import { authStorageGet } from "@/core/auth/scoped-storage";
|
import { authStorageGet } from "@/core/auth/scoped-storage";
|
||||||
|
import { normalizeExternalAccessToken } from "@/core/auth/external-access-token";
|
||||||
|
|
||||||
/** iframe 可挂载的父菜单(取自 core/page-layout/sidebar-menu.ts 的现有菜单 id)。 */
|
/** iframe 可挂载的父菜单(取自 core/page-layout/sidebar-menu.ts 的现有菜单 id)。 */
|
||||||
export const MOUNT_OPTIONS: { label: string; value: string }[] = [
|
export const MOUNT_OPTIONS: { label: string; value: string }[] = [
|
||||||
@@ -142,6 +143,9 @@ export function resolveRouteParamValue(
|
|||||||
}
|
}
|
||||||
if (param.source) {
|
if (param.source) {
|
||||||
const value = userInfo[param.source];
|
const value = userInfo[param.source];
|
||||||
|
if (param.source === "accessToken") {
|
||||||
|
return normalizeExternalAccessToken(value);
|
||||||
|
}
|
||||||
return value == null ? "" : String(value);
|
return value == null ? "" : String(value);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
|
|||||||
Reference in new issue
Block a user