5.5 KiB
登录鉴权扩展接口文档(auth_login)
在原有登录之上新增两种自定义登录模式,均由 config.yaml 的 auth_login 段控制。
配置
auth_login:
# 功能一:用户名直登口令门
username_login:
require_password: false # true 时 /login/username 必须带 ?password=
password: "" # 共享口令;留空默认 123ewq
# 功能二:token 换登录
token_login:
enabled: false # 总开关
token_info_url: "https://ch1.b.uat.4.cn/consumer/login/getTokenInfo"
service_authorization: "Bearer admin" # 调上游接口的鉴权头
timeout_seconds: 10
verify_ssl: false # 上游 HTTPS 证书校验;UAT/内网自签证书需置 false,生产置 true
ca_cert_path: "" # 可选:PEM 格式 CA 包路径,设置后优先生效并强制校验
HTTPS 注意:
token_info_url是 HTTPS。若上游用自签名 / 私有 CA 证书(UAT、内网常见),verify_ssl: true时 httpx 的 TLS 握手会失败,导致 token 登录恒定 401。此时应:
- 临时/内网:
verify_ssl: false(跳过证书校验);- 正规做法:把私有 CA 证书导出为 PEM,配
ca_cert_path: /path/to/ca.pem(优先于verify_ssl,仍做校验);- 生产对外域名(有效证书):保持
verify_ssl: true。握手失败时后端日志会打印具体异常类型(如
SSLError)与当前verify取值,便于排查。
配置模型:packages/harness/deerflow/config/login_config.py。修改 config.yaml 后无需重启(get_app_config() 按 mtime 自动重载)。
功能一:用户名直登 + 口令门
在原 POST /api/v1/auth/login/username 上叠加一个共享口令校验。require_password=false(默认)时行为完全不变(免密直登 + 自动注册)。
请求
POST /api/v1/auth/login/username[?password=<共享口令>]
Content-Type: application/json
{ "username": "alice" }
?password=:仅当require_password=true时需要。值需等于username_login.password;该项留空时默认口令为123ewq。- 口令门是单一共享口令,只决定"放不放行",不影响登录成的是哪个账号。
- 校验失败按 IP 计入登录限速(5 次/5 分钟锁定),与本地密码登录共用同一限速器。
响应
成功 200:
{
"access_token": "<JWT>",
"token_type": "bearer",
"expires_in": 604800,
"user_id": "<uuid>",
"email": "alice@cm.com",
"system_role": "user",
"needs_setup": false,
"created": false
}
同时种下 access_token HttpOnly Cookie。
失败:
| 状态码 | 场景 |
|---|---|
401 |
require_password=true 但口令缺失或不匹配 |
429 |
触发登录限速 |
前端入口
访问 /login/<用户名>?password=<口令>。LoginPage.tsx 从 ?password= 读取并透传给 loginByUsername。
功能二:token 换登录
前端用 URL 把上游 token 传进来,后端调上游接口换出用户名再登录。
请求
POST /api/v1/auth/login/token?token=<上游access_token>
后端据此向 token_info_url 发起(与下方 curl 对齐):前端传入的 token 直接拼成 Authorization: Bearer <token> 头,上游从该头读取令牌(不再用 cookie,也不再用静态 service_authorization)。
curl 'https://ch1.b.uat.4.cn/consumer/login/getTokenInfo' \
-X POST \
-H 'authorization: Bearer <上游access_token>' \
-H 'content-type: application/x-www-form-urlencoded'
上游返回示例:
{ "state": "200", "msg": "操作成功!", "data": { "username": "b1admin", "...": "..." } }
后端取 data.username → 经 _username_to_email 映射 → 库内查找(含跨域名 local-part 兜底)→ 查不到则自动注册 → 发本系统 JWT 并种 Cookie。
响应
成功 200:结构同功能一的 UsernameLoginResponse(首次自动注册时 created=true)。
失败:
| 状态码 | 场景 |
|---|---|
403 |
token_login.enabled=false |
401 |
上游调用失败 / HTTP 非 200 / state != "200" / 未取到 username |
429 |
触发登录限速 |
500 |
token_info_url 未配置 |
前端入口
访问 /login?authToken=<上游token>(用 authToken 而非 token,以避开既有的 tools_token 参数 ?token=)。LoginPage 命中 authToken 时走 loginByToken,不再调用户名直登。
安全说明
- 两个端点都在
auth_middleware._PUBLIC_EXACT_PATHS白名单内(免会话鉴权,自身完成鉴权)。 - 口令门是最低限度的共享密钥防护,适合内网/受控环境;非每用户独立密码。
- token 换登录信任上游
getTokenInfo的鉴权结论,务必保证token_info_url配置正确且走内网/HTTPS。前端传入的 token 会直接作为Authorization: Bearer <token>发给上游,因此该 token 必须来自可信入口。(service_authorization配置项已不再参与请求,保留仅为兼容,后续可移除。)
相关代码
- 后端端点 / 助手:
app/gateway/routers/auth.py(login_by_username、login_by_token、_get_or_create_user_by_username、_fetch_username_from_token) - 白名单:
app/gateway/auth_middleware.py - 配置模型:
packages/harness/deerflow/config/login_config.py(挂载于AppConfig.auth_login) - 前端:
frontend-web/src/core/auth/api.ts、frontend-web/src/pages/LoginPage.tsx - 测试:
tests/test_login_config.py、tests/test_auth_login_modes.py