feat(task-buttons): 跳转按钮可追加登录态 token+username + token 登录离线测试映射

rwfx/3qfx/xdfx 任务工作区的可配置跳转按钮(按钮管理)新增「追加登录态」开关:
url 类型按钮可在外链后拼 ?token=<token1>&username=<登录名>(参数名可配,默认
token/username),供跳转目标外部系统识别当前登录用户。

- 仅 token 登录(login/token)生效: token1 = ?authToken=,username = 后端用
  token 调 getTokenInfo 解析出的原始登录名(非邮箱前缀/非解 JWT)。二者登录时
  经 persistLoginCredentials 存入 login.token1/login.username(scoped-storage
  自动隔离嵌入/独立会话);非 token 登录读不到 token1 则跳过追加。
- 后端 UsernameLoginResponse 回传 username(原始名,规避邮箱归一化有损);
  token_login 加 mock_tokens 离线测试映射(123ewq->admin),命中即跳过外网
  getTokenInfo,便于本地全链路联调。
- task_buttons 加列 append_auth/auth_token_param/auth_name_param
  (migration 20260625_01 + 幂等 SQL)。

Tests: test_task_buttons.py, test_auth_login_modes.py(mock token/username 回传)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.8 committed 2026-06-25 14:52:07 +08:00
1 parent 6989dd45f1
commit f8b40353ef
18 files changed
+298 -6

No files matched your search

+4
View File
@@ -33,6 +33,7 @@ import {
} from '@/core/page-layout/sidebar-enabled';
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';
const DEFAULT_USERNAME = 'guest';
@@ -167,6 +168,9 @@ export default function LoginPage() {
// 持久化 token1(走 authStorage,嵌入态自动隔离)+ 预热上报配置 + 上报登录。
if (authToken) {
persistYLogToken(authToken);
// 存下 token1 + 登录用户名(getTokenInfo 解析出的原始名,后端回传),供任务工作区
// 跳转按钮的「追加登录态」拼到外链上(仅 token 登录才有这两个值)。
persistLoginCredentials(authToken, auth);
void fetchYLogConfig();
writeYLog({
optType: OptType.login,
@@ -105,6 +105,9 @@ export default function TaskButtonsPage() {
idParam: "id",
idKind: "task",
openMode: "blank",
appendAuth: false,
authTokenParam: "token",
authNameParam: "username",
enabled: true,
sortOrder: maxSort + 1,
},
@@ -319,6 +322,37 @@ export default function TaskButtonsPage() {
<SelectItem value="self">当前窗口</SelectItem>
</SelectContent>
</Select>
<label
className="text-muted-foreground ml-2 flex shrink-0 items-center gap-1.5 text-xs"
title="跳转时在链接后追加 token + 用户名(仅 token 登录时生效)"
>
<Switch
checked={r.appendAuth}
onCheckedChange={(v) => update(r.id, { appendAuth: v })}
/>
追加登录态
</label>
{r.appendAuth && (
<div className="flex shrink-0 items-center gap-1 text-xs">
<span className="text-muted-foreground">&amp;</span>
<Input
value={r.authTokenParam}
onChange={(e) => update(r.id, { authTokenParam: e.target.value })}
placeholder="token"
className="h-8 w-24"
title="token 的查询参数名(默认 token)"
/>
<span className="text-muted-foreground">=token1 &amp;</span>
<Input
value={r.authNameParam}
onChange={(e) => update(r.id, { authNameParam: e.target.value })}
placeholder="username"
className="h-8 w-28"
title="用户名的查询参数名(默认 username)"
/>
<span className="text-muted-foreground">=用户名</span>
</div>
)}
</>
) : (
<Select value={r.target} onValueChange={(v) => update(r.id, { target: v })}>