fix(登录): token 登录补全 userInfo + 全方式打印路由参数
- 把「拉外部用户信息(yUserId→consumer)」与登录鉴权方式解耦:只要 URL 带 yUserId 就填充 userInfo(含 token 登录,consumer 失败非致命),修复 token 登录后 userInfo 残缺、iframe/轻应用拿不到 yUserId/userName/gateDepartName/ typeName/accessToken 的问题。 - token 登录用 auth.username 写入正确用户名(不覆盖 consumer 已落的 userName)。 - 保留 yUserId 优先级(onlineTicket/toolToken 仅在无 yUserId 时启用)。 - 新增登录路由参数 + 落地结果日志(令牌脱敏),方便排查各登录方式取值问题。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
6378cd33bd
commit
537e2e4d06
1 file changed
+93
-4
@@ -35,6 +35,35 @@ import { fetchYLogConfig } from '@/core/log/y-log-config';
|
||||
import { persistYLogToken } from '@/core/log/y-log-token';
|
||||
import { persistLoginCredentials } from '@/core/auth/login-credentials';
|
||||
import { OptType, writeYLog } from '@/core/log/y-log';
|
||||
import { authStorageGet } from '@/core/auth/scoped-storage';
|
||||
|
||||
/** 令牌脱敏:日志里只露头尾,避免完整令牌泄漏。 */
|
||||
function maskLoginToken(value: string): string {
|
||||
if (!value) return '';
|
||||
if (value.length <= 10) return `${value.slice(0, 2)}***(${value.length})`;
|
||||
return `${value.slice(0, 6)}...${value.slice(-4)}(${value.length})`;
|
||||
}
|
||||
|
||||
/** 读取已落地的 userInfo(脱敏 access token 后)供登录后排查。 */
|
||||
function summarizeStoredUserInfo(): Record<string, unknown> | null {
|
||||
try {
|
||||
const raw = authStorageGet('userInfo');
|
||||
if (!raw) return null;
|
||||
const info = JSON.parse(raw) as Record<string, unknown>;
|
||||
return {
|
||||
keys: Object.keys(info),
|
||||
yUserId: info.yUserId,
|
||||
userId: info.userId,
|
||||
userName: info.userName,
|
||||
gateDepartName: info.gateDepartName,
|
||||
typeName: info.typeName,
|
||||
access_token: maskLoginToken(String(info.access_token ?? '')),
|
||||
accessToken: maskLoginToken(String(info.accessToken ?? '')),
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_USERNAME = 'guest';
|
||||
const IFRAME_LOGIN_SOURCE = 'iframe';
|
||||
@@ -171,19 +200,68 @@ export default function LoginPage() {
|
||||
async function runLogin() {
|
||||
setStatus('loading');
|
||||
setErrorMessage('');
|
||||
|
||||
// ── 调试:登录路由参数全打印 ──────────────────────────────────────────────
|
||||
// token / yUserId / 用户名 / ticket 任一方式都打印(令牌脱敏),方便排查
|
||||
// “userInfo 取不到值 / 传值不对” 一类问题。`embed=0`/正常态也照打。
|
||||
const loginMethod = authToken
|
||||
? 'token(authToken)'
|
||||
: yUserId
|
||||
? 'yUserId'
|
||||
: onlineTicket
|
||||
? 'onlineTicket'
|
||||
: toolToken
|
||||
? 'toolsToken'
|
||||
: 'username';
|
||||
console.info('[login] route params', {
|
||||
method: loginMethod,
|
||||
pathUsername: resolvedUsername,
|
||||
yUserId,
|
||||
access_token: maskLoginToken(access_token),
|
||||
toolToken: maskLoginToken(toolToken),
|
||||
authToken: maskLoginToken(authToken),
|
||||
gatePassword: gatePassword ? maskLoginToken(gatePassword) : '',
|
||||
onlineTicket: onlineTicket ? maskLoginToken(onlineTicket) : '',
|
||||
taskId,
|
||||
goPath,
|
||||
redirect: redirectTo,
|
||||
isEmbed,
|
||||
rawSearch: searchParams.toString(),
|
||||
});
|
||||
|
||||
try {
|
||||
let auth;
|
||||
let loginUserName = '';
|
||||
|
||||
// 外部用户信息(yUserId → consumer 用户详情)与登录鉴权方式**解耦**:只要 URL
|
||||
// 带 yUserId 就拉一次 userDetailByYuserId,把 yUserId/userName/gateDepartName/
|
||||
// typeName/accessToken 落进 userInfo。之前 token(authToken) 登录会短路掉这一步,
|
||||
// 导致 token 登录后 userInfo 残缺(只剩 userName)、iframe/轻应用拿不到这些值。
|
||||
if (yUserId) {
|
||||
if (authToken) {
|
||||
// token 登录下 consumer 失败不应阻断登录(仅影响 userInfo 的富信息)。
|
||||
try {
|
||||
await loginByYUserId(yUserId, toolToken || null, access_token || null);
|
||||
} catch (error) {
|
||||
console.warn('[login] loginByYUserId failed under token login (non-fatal)', error);
|
||||
}
|
||||
} else {
|
||||
await loginByYUserId(yUserId, toolToken || null, access_token || null);
|
||||
}
|
||||
}
|
||||
|
||||
if (authToken) {
|
||||
// token 换登录:后端用 authToken 调上游接口换出用户名再登录
|
||||
auth = await loginByToken(authToken);
|
||||
// 让 token 登录也写入正确用户名(后端 getTokenInfo 解析出的原始登录名)。
|
||||
// 若上面 yUserId 已从 consumer 落了 userName,则 setStoredUserName 不会覆盖它。
|
||||
loginUserName = auth.username?.trim() ?? '';
|
||||
} else {
|
||||
let deerflowUsername = resolvedUsername;
|
||||
if (yUserId) {
|
||||
await loginByYUserId(yUserId, toolToken || null, access_token || null);
|
||||
} else if (onlineTicket) {
|
||||
// yUserId 优先(已在上面落好 userInfo + tools_token);其余方式仅在无 yUserId 时启用。
|
||||
if (!yUserId && onlineTicket) {
|
||||
deerflowUsername = await resolveUsernameByOnlineTicket(onlineTicket);
|
||||
} else if (toolToken) {
|
||||
} else if (!yUserId && toolToken) {
|
||||
persistToolsToken(toolToken);
|
||||
}
|
||||
|
||||
@@ -217,6 +295,17 @@ export default function LoginPage() {
|
||||
if (!yUserId) {
|
||||
persistAccessToken(access_token);
|
||||
}
|
||||
|
||||
// ── 调试:登录落地结果(auth + 实际写入的 userInfo,access token 已脱敏) ──
|
||||
console.info('[login] stored result', {
|
||||
method: loginMethod,
|
||||
user_id: auth.user_id,
|
||||
email: auth.email,
|
||||
authUsername: auth.username,
|
||||
loginUserName,
|
||||
userInfo: summarizeStoredUserInfo(),
|
||||
});
|
||||
|
||||
// 嵌入模式不写 sidebarEnabled(localStorage 的「简洁/全量」偏好会跨标签污染正常页);
|
||||
// 去 chrome 由按标签页的嵌入会话(isEmbedActive)驱动,见 PageLayout。
|
||||
if (!isEmbed) persistSidebarEnabled(sidebarEnabled);
|
||||
|
||||
Reference in new issue
Block a user