Files
lionwebsite-backend/docs/login-command-plan.md
T

8.2 KiB
Raw Blame History

LionWebsite 一键登录指令方案(/login)

状态:待用户确认,未实施。 关联待办:todos/open-td-20260921-a1b2c3-lionwebsite-login-tg.md。

本方案对齐 PersonalHub 已有的面板登录实现(git.lionwebsite.xyz/lion/PersonalService, personal_service/app.py + personal_service/security.py)。PersonalHub 已经用同一套 机制跑在生产上,机器人命令 /login、一次性票据、Cookie 会话、退出端点都齐全,因此 LionWebsite 侧直接照搬即可,不需要发明新协议。

1. 现状

  • 个人面板入口是 https://personal.lionwebsite.xyz/index(桌面)与 /mobile(移动)。 nginx 把 / 代理到后端 /personal/,把 /index、/mobile 映射到静态入口文件。
  • 面板没有登录页。PrivateMain 与 PrivateMainForMobile 的 src/store/index.js 把 authCode: "alone" 写死,所有请求都带 ?AuthCode=alone。
  • 后端 PersonalInterceptor 对 /personal/**、/remote/** 直接比较字面量 alone; AdaptorFilter 也用它决定移动端跳转。/remote/** 目前没有对应控制器,属历史遗留。
  • alone 不在 User 表里(表内是 big lion / pubraseer / temp / au283602 / 0619 / liondown / bigcat)。它是独立的固定字面量:不轮换、不区分人,且随 JS 产物公开。
  • 存储节点另有两处写死 alone:storageNode 的 CustomUtil.java(向 /message2me 推送)与 MultiThreadedHTTPServer.java(本机 HTTP 鉴权)。

2. 对照:PersonalHub 现有实现

关键点 PersonalHub 做法 LionWebsite 对应做法
票据 OneTimePanelLoginTickets:进程内字典,secrets.token_urlsafe(32),TTL 300 秒,consume 即弹出,最多 8 张活跃 同样进程内一次性票据,参数可配
存法 只存 SHA-256 摘要,不存明文 相同
链接 panel_login_link() 只允许配置里的固定 HTTPS origin,附加 ?token= https://personal.lionwebsite.xyz/login?t=
落地点 GET /panel/telegram-login 校验票据 → 写 session → 303 跳 /panel GET /login 校验票据 → 写会话 → 302 跳 /index
会话 Starlette SessionMiddleware,签名 Cookie,same_site=strict,14 天 Spring Boot HttpSession + Tomcat Cookie,或自签 Cookie
口令登录 另有一条 /api/panel/login:口令 + TOTP,带 AttemptLimiter(5, 300) 可选,见下文第 4 节
退出 POST /api/panel/logout 清 session GET /login/logout
机器人 TELEGRAM_COMMANDS 加 login,telegram.py 派发到 telegram_panel_login() QQ / TG 各加 /login
未配置时 返回「个人面板尚未完整配置,无法生成登录链接。」 返回同类中文提示,不抛异常

关键差异:PersonalHub 是 ASGI,直接有 request.session;LionWebsite 是 Spring Boot, 需要自己选会话载体。推荐直接用 Spring 的 HttpSession,由容器签发 JSESSIONID Cookie,省去自签实现;PersonalHub 的签名 Cookie 是为了在 ASGI 侧无需额外依赖。

3. 推荐方案:一次性票据换会话

沿用 PersonalHub 的形状,票据一次性、短时,会话 Cookie 长期。

3.1 后端(lionwebsite-backend,host-us9929)

  1. 新增 PanelLoginTickets:进程内 ConcurrentHashMap,值为过期时间戳;签发 SecureRandom 32 字节 base64url;校验时先移除再判断,保证一次性;同时清理过期项 并限制活跃上限(防内存增长)。
  2. 新增登录端点。nginx 无需改动:location / 已把请求改写成 /personal/...,因此 https://personal.lionwebsite.xyz/login?t=... 会落到后端 /personal/login。
    • GET /login?t=<ticket>:命中票据 → 建立会话(session.setAttribute("personalAuthenticated", true)) → 302 跳 /index;未命中或被复用 → 302 跳 /personal/denied(或回登录提示页)。
    • GET /login/logout:session.invalidate() → 跳回提示页。
  3. PersonalInterceptor 放行条件改为「有效会话 或 合法 AuthCode」。过渡期保留 AuthCode,避免影响下载器前端与存储节点推送。
  4. InterceptorConfiguration 必须把 /personal/login、/personal/login/logout 排除在 PersonalInterceptor 之外,否则登录端点会被自己拦住。
  5. 票据 TTL 与上限走 application.yaml 配置项,便于调整;不涉及密钥,无需秘密库。
  6. 登录失败写日志(时间、来源 IP、结果),成功也记一行,便于回溯谁在何时登录。

3.2 机器人(QQ + Telegram,host-vm103-debian-qq)

PersonalHub 已经具备生成链接的全部能力,但它签的是自己的面板票据。给 LionWebsite 用有两种接法:

  • 接法 A(推荐,改动最小):PersonalHub 复用已有的 _login_handler 形状,新增一个 「生成 LionWebsite 登录链接」的处理函数。它需要拿到一张 LionWebsite 票据,因此 LionWebsite 侧要提供一个受保护的签发端点,例如 POST /personal/login/ticket, 用现有共享盘内部令牌或固定内部密钥鉴权,返回一次性票据;PersonalHub 调用后拼成链接。
  • 接法 B(无新增网络调用):两端约定一个共享密钥,PersonalHub 本地用 HMAC-SHA256(密钥, 时间戳) 自签票据,LionWebsite 校验签名与时间窗。省一次调用, 但票据在时间窗内可重放,需要额外一次性状态才能封住;而 PersonalHub 现有 OneTimePanelLoginTickets 本来就是进程内一次性表,接法 A 更贴近既有做法。

两种接法下,QQ 与 TG 都走同一个处理函数,TELEGRAM_COMMANDS 加 login, QQ 侧加 /login(别名 /登录),回复文案沿用 PersonalHub 的 「已生成一次性登录链接,X 分钟内有效且只能使用一次……请勿转发」。

跨主机连通性:us9929 与 vm103 之间已有 EasyTier(us9929 侧 10.0.0.6, vm103 即 192.168.0.204),且 personal.lionwebsite.xyz 直接解析到 us9929 公网 IP 38.60.92.138,不经过 Cloudflare。因此接法 A 的内网调用与浏览器打开链接都不需要 额外放通。

3.3 前端(PrivateMain 桌面 / PrivateMainForMobile 移动)

  • 删除 authCode: "alone" 常量与 ?AuthCode= 查询串,改为依赖同源 Cookie (axios 同源请求默认携带 Cookie)。
  • 未登录或会话过期时显示提示页「登录已过期,请在机器人里发送 /login」,不要静默失败。
  • 下载器前端(lionwebsite-frontend-desktop / -mobile)使用每个用户自己的授权码, 本次不动。

3.4 收尾:退役 alone

  • PersonalInterceptor、AdaptorFilter 不再比较字面量;移动端跳转改为原样透传查询串。
  • 存储节点两处 alone 换成配置项,属 storageNode 仓库,单独一次发布。
  • 轮换后确认无调用方仍依赖旧字面量。

4. 备选与取舍

  • 方案 B(最小改动):前端增加「从 URL 读取 AuthCode 并记住」,机器人 /login 直接回 https://personal.lionwebsite.xyz/index?AuthCode=alone。半天可上线,但固定密钥仍会进 聊天记录、浏览器历史与 nginx 日志,也没解决 alone 写死的问题。
  • 方案 C(只治理配置):把 alone 从代码搬到配置并轮换,安全提升有限。
  • 口令 + TOTP 登录页:PersonalHub 有这一套,但用户明确要的是「输入 /login 就弹出登录 地址」,口令 TOTP 属于另一条路径。如果以后想在电脑上直接登录而不经机器人,可以再补, 两者共用同一个会话状态即可。

5. 需要确认的三点

  1. 采用推荐方案(一次性票据 + 会话),还是先上备选 B?
  2. 登录链接只允许主人使用,还是允许机器人给其他授权用户分别签发(对应 User 表账号)?
  3. 会话有效期:PersonalHub 是 14 天,建议 LionWebsite 也用 14 天滑动过期,可调整。

6. 实施顺序

  1. 后端票据签发与会话校验、拦截器放行、退出端点,含单元测试。
  2. 后端受保护的票据签发端点(接法 A)或共享密钥签名(接法 B)。
  3. 前端去掉 alone,补未登录提示。
  4. 机器人 QQ 与 Telegram 双向 /login。
  5. 存储节点两处 alone 改为配置。
  6. 退役字面量 alone。