login_api_audit.md 8.1 KB

登录后接口校验与登出子进程排查报告

一、排查结论概览(已改造后)

维度 结论 说明
接口是否校验登录 是 需登录的接口由后端统一校验 session(选品配置/选品开始已改用 verify_session_with_machine_and_expiry)
接口是否校验机器码 是 心跳、选品配置、选品开始等接口均要求传 machine_code 并与 session 绑定一致,否则 403
接口是否判断用户过期 是 上述接口在后端统一校验用户卡密/订阅未过期,过期返回 403
发生上述情况是否退出登录 是 客户端对 401/403 统一走 handleAuthError → 退登并跳转登录页
退出登录是否关闭子进程 是 登出时已调用 stop_all_processes(),会尝试远程停止选品与扫货任务(selection-stop / sweep-stop),失败仅记录日志不阻断登出

二、机器码校验:客户端传什么、后端怎么校验

客户端传给后端的数据

  • 来源:authStore.machineCode 来自 Rust get_machine_code()(即 machine::generate_machine_code())。
  • 格式:原始机器特征字符串(raw),当前为 DISK=<硬盘序列号>(例如 DISK=WD-WMAZA1234567)。多盘时取第一个磁盘序列号;不含 MAC,避免换网卡/IP 导致同机不同码。详见 client-tauri/src-tauri/src/machine.rs。

加密接口(心跳、keys/sync、user/profile)

  • 请求体为 AES+RSA 加密,解密后的 params 里带 raw_machine_info(或 machine_code)即上述 raw 字符串。
  • 后端不信任客户端传入的 machine_code 字面值:从解密参数里取 raw_machine_info / machine_code,用 raw_machine_info_to_machine_code(raw) 做规范化 + MD5,得到 32 位小写 hex,再与 session.machine_code(登录时存的也是该 MD5)比较。

明文接口(选品配置、filter-options、goods-attr-list、selection-start)

  • 客户端在 URL 或 body 里传的 machine_code 即为上面的 raw(authStore.machineCode = get_machine_code())。
  • 后端 session 里存的是 MD5(登录时由后端对 raw 做规范化+MD5 后写入)。
    为统一校验逻辑,verify_session_with_machine_and_expiry 会对入参做统一规范化:
    • 若传入已是 32 位小写 hex,视为已 MD5,直接与 session.machine_code 比较;
    • 否则视为 raw(如 DISK=xxx),先 raw_machine_info_to_machine_code(mc) 得到 MD5,再与 session.machine_code 比较。

这样无论客户端传 raw 还是 MD5,后端都能与登录时绑定的机器码一致校验。


三、后端校验情况(改造后)

  • 登录/心跳:与改造前一致,完整校验会话、机器码、用户状态、用户卡密过期。
    • auth_service.login、heartbeat_service.heartbeat。
  • 选品配置与选品开始(已改造):
    • AuthService.verify_session_with_machine_and_expiry(session_id, machine_code):统一校验会话有效 + 机器码与 session 一致 + 用户未禁用 + 用户卡密未过期。
    • 使用该方法的接口:POST/GET /buff/selection-config、POST /buff/selection-start,请求体/查询参数均要求传 session_id 与 machine_code。
    • 不通过时返回 401(会话过期/用户不存在)或 403(机器码不匹配/账号禁用/卡密过期),客户端会退登并跳转登录。

四、客户端接口与 401/403 处理

3.1 已统一处理 401/403(会退登并跳转登录)

  • secureApiClient.login:401/403 → handleAuthError
  • secureApiClient.recharge:401/403 → handleAuthError
  • secureApiClient.unbind:401/403 → handleAuthError
  • secureApiClient.changePassword:错误信息含 401/403/会话 → handleAuthError
  • secureApiClient.heartbeat:401/403 或 catch 到会话相关错误 → handleAuthError
  • secureApiClient.getProfile:401/403 → handleAuthError
  • 本次新增:secureApiClient.getBuffSelectionConfig:401/403 → handleAuthError
  • 本次新增:secureApiClient.saveBuffSelectionConfig:401/403 → handleAuthError

3.2 无需登录或无需退登的接口

  • getAnnouncements:公告接口,未做 401/403 退登(按设计可为公开)。
  • getClientConfigs:后端为“无需登录”的配置接口,未做 401/403 退登。
  • getBuffFilterOptions / getBuffGoodsAttrList:已改为需登录,后端校验 session + machine_code + 用户过期,前端传参并做 401/403 → handleAuthError。
  • syncHmacKey(密钥同步):后端已校验用户+机器码+卡密过期,前端 401/403 → handleAuthError。

3.3 登出与事件

  • handleAuthError 会:停止心跳、调用 authStore.logout()、派发 auth-error,由 App.vue 统一 router.replace('/login')。
  • 应用启动时若“会话预检”(心跳)失败,也会退登并跳转登录。

五、登出时是否关闭子进程

  • Rust 侧:user_logout 顺序为:
    1. 调用 stop_all_processes()(停止客户端 exe 开启的子进程,如选品、扫货);
    2. 调用 stop_heartbeat_service();
    3. 调用后端 logout 接口。
  • 当前:stop_all_processes() 已实现并在登出时被调用,内部会调用后端安全接口停止选品与扫货任务(/api/v1/buff/secure/selection-stop、/api/v1/buff/secure/sweep-stop)。
  • 建议:保持“尽最大努力停止”策略(失败不阻断登出),并持续完善停止链路的可观测日志,便于问题追踪。

六、本次代码修改摘要

后端 (backend-client)

  1. app/services/auth_service.py

    • 新增 verify_session_with_machine_and_expiry(session_id, machine_code, software_code=None):校验会话存在且未过期、请求机器码与 session 一致、用户存在且未禁用、用户卡密未过期;返回 (session, err_code, err_msg),失败时为 401/403。
  2. app/api/v1/router.py

    • POST /buff/selection-config:请求体增加必填 machine_code,改用 verify_session_with_machine_and_expiry,不通过返回 401/403。
    • GET /buff/selection-config:查询参数增加必填 machine_code,同上。
    • POST /buff/selection-start:请求体增加必填 machine_code,同上。

前端 (client-tauri)

  1. src/utils/secureApiClient.ts

    • getBuffSelectionConfig(session_id, machine_code):增加参数 machine_code,请求 URL 带 machine_code;响应 401/403 时调用 handleAuthError。
    • saveBuffSelectionConfig:参数增加必填 machine_code,请求体带 machine_code;响应 401/403 时调用 handleAuthError(此前已加)。
  2. src/views/BuffSelectionView.vue

    • 加载/保存选品配置时从 authStore.machineCode 取值,调用 getBuffSelectionConfig(sessionId, machineCode) 与 saveBuffSelectionConfig(..., machine_code: machineCode)。
  3. src-tauri/src/commands.rs

    • user_logout:在停止心跳之前先调用 stop_all_processes().await(错误仅打日志,不阻断登出),保证登出时尝试关闭子进程。

七、补充改造(登录后全部业务接口三项校验)

  • 心跳、密钥同步(keys/sync)、用户信息(user/profile):已通过 @secure_endpoint(require_login=True, require_machine_code=True) 做会话+机器码+用户状态+用户卡密过期校验,不通过返回 401/403。
  • HMAC 密钥同步(keys/hmac):后端 validate_user_for_hmac 增加用户卡密/订阅过期校验,过期返回 403;前端 syncHmacKey 在 401/403 时调用 handleAuthError 触发退登。
  • BUFF 筛选项(filter-options)、商品属性(goods-attr-list):后端增加必填 session_id、machine_code,使用 verify_session_with_machine_and_expiry 做三项校验;前端传参并在 401/403 时 handleAuthError,选品页调用处传入 authStore.sessionId、authStore.machineCode。

文档生成日期:2026-03-01;改造完成:选品相关接口已统一“登录+机器码+过期”校验并触发退登,登出时已调用 stop_all_processes;登录后业务接口(含心跳、密钥交换、选品)均已三项校验且 401/403 退登。