登录鉴权:新增用户名口令门 + token 换登录两种模式

- config 新增 auth_login 段(login_config.py):
  - username_login.require_password 开启后,用户名直登必须带 ?password=,口令留空默认 123ewq
  - token_login 用上游 token 调 getTokenInfo 换用户名再登录,用户名不在库自动注册;含 verify_ssl/ca_cert_path 处理 HTTPS 自签证书
- auth.py:口令门、/login/token 端点、共享 _get_or_create_user_by_username 与 _fetch_username_from_token;登录类报错文案改中文
- auth_middleware:/login/token 加入免鉴权白名单
- 前端 api.ts/LoginPage:透传 ?password=、?authToken=;修复错误体为对象时显示 [object Object] 的问题
- 文档:backend/CLAUDE.md + docs/AUTH_LOGIN.md 接口说明

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.8 committed 2026-06-02 17:39:56 +08:00
1 parent 5aa1fdca04
commit c484481964
10 files changed
+531 -56

No files matched your search

+18 -8
View File
@@ -4,7 +4,7 @@ import { useParams, useRouter, useSearchParams } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useTheme } from "@/contexts/ThemeContext";
import { loginByUsername } from "@/core/auth/api";
import { loginByToken, loginByUsername } from "@/core/auth/api";
import { setStoredAuth } from "@/core/auth";
import {
persistExternalLoginParams,
@@ -40,6 +40,10 @@ export default function LoginPage() {
// 外部跳转参数:theme 直接调 setTheme;ticket/userId/env 持久化到 localStorage
const yUserId = searchParams.get("yUserId")?.trim() ?? "";
const toolToken = searchParams.get("token")?.trim() ?? "";
// 用户名直登口令门:开启后端开关时,口令从 ?password= 透传
const gatePassword = searchParams.get("password") ?? "";
// token 换登录:?authToken= 传上游 token(与 tools_token 的 ?token= 区分开)
const authToken = searchParams.get("authToken")?.trim() ?? "";
const externalParams = useMemo(() => ({
theme: searchParams.get("theme") ?? "",
ticket: searchParams.get("ticket") ?? "",
@@ -71,13 +75,19 @@ export default function LoginPage() {
setStatus("loading");
setErrorMessage("");
try {
if (yUserId) {
await loginByYUserId(yUserId, toolToken || null);
} else if (toolToken) {
persistToolsToken(toolToken);
}
let auth;
if (authToken) {
// token 换登录:后端用 authToken 调上游接口换出用户名再登录
auth = await loginByToken(authToken);
} else {
if (yUserId) {
await loginByYUserId(yUserId, toolToken || null);
} else if (toolToken) {
persistToolsToken(toolToken);
}
const auth = await loginByUsername(resolvedUsername);
auth = await loginByUsername(resolvedUsername, gatePassword);
}
if (cancelled) {
return;
}
@@ -100,7 +110,7 @@ export default function LoginPage() {
return () => {
cancelled = true;
};
}, [resolvedUsername, router, sidebarEnabled, yUserId, toolToken]);
}, [resolvedUsername, router, sidebarEnabled, yUserId, toolToken, authToken, gatePassword]);
return (
<div className="flex min-h-screen items-center justify-center bg-background px-6">