用户认证系统.md 15 KB

用户认证系统

本文档引用的文件

  • src/index.ts
  • src/command/index.ts
  • src/client/cosmoe.ts
  • src/command/handlers/login.ts
  • src/command/handlers/logout.ts
  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts
  • src/command/handlers/events.ts
  • src/command/handlers/eventDetails.ts
  • wrangler.jsonc
  • package.json

目录

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能考量
  8. 故障排除指南
  9. 结论

简介

本项目是一个基于 Cloudflare Workers 的 Telegram 机器人,提供用户认证与活动预约功能。认证系统采用交互式对话完成用户名/密码登录,通过 Cosmoe API 获取 Token 并持久化到 KV 存储;后续需要授权的操作(如查看历史、预约活动)均需使用已存储的凭证进行鉴权。系统未实现中间件层,而是通过各命令处理器在执行前主动检查凭证有效性。

项目结构

项目采用按职责分层的组织方式:

  • 入口与路由:src/index.ts 负责初始化 Bot、注册命令菜单与 Webhook 回调
  • 命令与对话:src/command/index.ts 安装对话插件、注册所有命令与回调处理器
  • 认证客户端:src/client/cosmoe.ts 提供与外部 Cosmoe API 的交互能力,包括 Token 获取与受保护接口调用
  • 处理器:src/command/handlers 下的各文件分别处理不同命令与业务流程
  • 配置:wrangler.jsonc 定义 Worker 名称、触发器、KV 绑定等运行时配置

    graph TB
    A["入口: src/index.ts"] --> B["命令注册: src/command/index.ts"]
    B --> C["登录处理器: src/command/handlers/login.ts"]
    B --> D["登出处理器: src/command/handlers/logout.ts"]
    B --> E["历史查询: src/command/handlers/history.ts"]
    B --> F["活动预约: src/command/handlers/bookEvent.ts"]
    B --> G["活动列表: src/command/handlers/events.ts"]
    B --> H["活动详情: src/command/handlers/eventDetails.ts"]
    C --> I["认证客户端: src/client/cosmoe.ts"]
    D --> I
    E --> I
    F --> I
    G --> I
    H --> I
    A --> J["配置: wrangler.jsonc"]
    B --> K["依赖: package.json"]
    

图表来源

  • src/index.ts
  • src/command/index.ts
  • src/client/cosmoe.ts
  • src/command/handlers/login.ts
  • src/command/handlers/logout.ts
  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts
  • src/command/handlers/events.ts
  • src/command/handlers/eventDetails.ts
  • wrangler.jsonc
  • package.json

章节来源

  • src/index.ts
  • src/command/index.ts
  • wrangler.jsonc
  • package.json

核心组件

  • Bot 与环境绑定:入口文件初始化 Bot 并注入 KV 命名空间,同时设置命令菜单与 Webhook 回调
  • 对话系统:通过 @grammyjs/conversations 插件与 KV 适配器实现对话状态持久化
  • 认证客户端:封装与 Cosmoe API 的交互,支持获取 Token、设置凭证、判断认证状态以及调用受保护接口
  • 登录处理器:交互式对话收集用户名/密码,调用认证客户端获取 Token,并将 user_id、token 与时间戳写入 KV
  • 登出处理器:根据 Telegram 用户 ID 读取并删除 KV 中的凭证
  • 历史与预约处理器:在执行前从 KV 读取凭证,设置到认证客户端,再调用受保护接口

章节来源

  • src/index.ts
  • src/command/index.ts
  • src/client/cosmoe.ts
  • src/command/handlers/login.ts
  • src/command/handlers/logout.ts
  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts

架构总览

下图展示了认证系统的关键交互路径:用户通过 /login 进入对话,输入凭据后由认证客户端向外部 API 请求 Token,并将凭证存入 KV;后续命令在执行前从 KV 读取凭证并设置到认证客户端以完成鉴权。

sequenceDiagram
participant U as "用户"
participant T as "Telegram Bot"
participant L as "登录处理器(login.ts)"
participant C as "认证客户端(cosmoe.ts)"
participant KV as "KV存储(COSMOE_CREDENTIALS)"
participant API as "Cosmoe API"
U->>T : "/login"
T->>L : 进入对话
L->>U : "请输入用户名"
U-->>L : 用户名
L->>U : "请输入密码"
U-->>L : 密码
L->>C : getToken(用户名, 密码)
C->>API : POST /CreatToken.php
API-->>C : 返回 {code, data : {user_id, token}}
C-->>L : 认证结果
alt 成功
L->>KV : put(Telegram用户ID, {user_id, token, timestamp})
L-->>U : "登录成功,凭证已存储"
else 失败
L-->>U : "登录失败:用户名或密码错误"
end

图表来源

  • src/command/handlers/login.ts
  • src/client/cosmoe.ts
  • wrangler.jsonc

章节来源

  • src/command/handlers/login.ts
  • src/client/cosmoe.ts
  • wrangler.jsonc

详细组件分析

登录流程(用户名/密码验证、Token 获取与存储)

  • 输入收集:通过对话等待用户输入用户名与密码,避免明文泄露
  • 凭证校验:调用认证客户端的 getToken 方法,向外部 API 发送表单数据
  • 成功分支:若返回 code=200 且包含 user_id 与 token,则将凭证写入 KV,键为 Telegram 用户 ID
  • 失败分支:提示用户名或密码错误
  • 异常处理:捕获并记录错误,向用户反馈“稍后重试”

    flowchart TD
    Start(["进入登录对话"]) --> AskUser["提示输入用户名"]
    AskUser --> GetUser["等待用户名输入"]
    GetUser --> CheckUser{"用户名存在?"}
    CheckUser --> |否| Cancel1["取消登录并提示"]
    CheckUser --> |是| AskPass["提示输入密码"]
    AskPass --> GetPass["等待密码输入"]
    GetPass --> CheckPass{"密码存在?"}
    CheckPass --> |否| Cancel2["取消登录并提示"]
    CheckPass --> |是| CallAPI["调用getToken获取Token"]
    CallAPI --> Result{"code=200且有token?"}
    Result --> |是| SaveKV["以Telegram用户ID为键保存凭证到KV"]
    SaveKV --> Done(["回复登录成功"])
    Result --> |否| Fail(["回复登录失败"])
    Cancel1 --> End(["结束"])
    Cancel2 --> End
    Done --> End
    Fail --> End
    

图表来源

  • src/command/handlers/login.ts

章节来源

  • src/command/handlers/login.ts
  • src/client/cosmoe.ts

登录后会话状态管理与凭证存储

  • KV 键设计:以 Telegram 用户 ID 作为键,值为包含 user_id、token、timestamp 的 JSON
  • 读取与设置:历史查询与预约等处理器在执行前从 KV 读取凭证,设置到认证客户端
  • 认证状态:认证客户端提供 isAuthenticated 与 getCredentials 方法用于判断与获取当前凭证

    classDiagram
    class CosmoeClient {
    -number userId
    -string token
    +getToken(username, password) ApiResponse
    +setCredentials(userId, token) void
    +getCredentials() {userId, token}|null
    +isAuthenticated() boolean
    }
    class LoginHandler {
    +handleInteractiveLogin(conversation, ctx, env) Promise
    }
    class HistoryHandler {
    +handleHistoryCommand(ctx, env) Promise
    }
    class BookEventHandler {
    +handleBookEvent(ctx, env) Promise
    }
    LoginHandler --> CosmoeClient : "获取Token并设置凭证"
    HistoryHandler --> CosmoeClient : "读取凭证并调用受保护接口"
    BookEventHandler --> CosmoeClient : "读取凭证并调用受保护接口"
    

图表来源

  • src/client/cosmoe.ts
  • src/command/handlers/login.ts
  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts

章节来源

  • src/command/handlers/login.ts
  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts
  • src/client/cosmoe.ts

登出流程(清理机制、安全考虑与最佳实践)

  • 身份确认:从上下文提取 Telegram 用户 ID
  • 凭证检查:读取 KV 中是否存在对应凭证
  • 删除操作:存在则删除该键,不存在则提示先登录
  • 反馈与异常:统一回复与错误日志记录

    sequenceDiagram
    participant U as "用户"
    participant T as "Telegram Bot"
    participant LO as "登出处理器(logout.ts)"
    participant KV as "KV存储(COSMOE_CREDENTIALS)"
    U->>T : "/logout"
    T->>LO : 处理登出请求
    LO->>LO : 获取Telegram用户ID
    LO->>KV : get(用户ID)
    alt 无凭证
    LO-->>U : "请先使用 /login 命令登录"
    else 有凭证
    LO->>KV : delete(用户ID)
    LO-->>U : "成功登出,账户信息已清除"
    end
    

图表来源

  • src/command/handlers/logout.ts
  • wrangler.jsonc

章节来源

  • src/command/handlers/logout.ts

认证状态检查与错误处理策略

  • 认证状态检查:历史查询与预约处理器在调用受保护接口前,先从 KV 读取凭证并设置到认证客户端,随后通过 isAuthenticated 判断
  • 错误处理:对 KV 读取失败、API 返回非 200、凭证无效等情况分别给出明确提示
  • 最佳实践:对敏感操作(如变更密码、取消预约)在处理器内部再次校验认证状态,确保安全性

    flowchart TD
    Enter(["进入受保护操作"]) --> ReadKV["从KV读取凭证"]
    ReadKV --> Parse{"解析成功?"}
    Parse --> |否| PromptLogin["提示先登录"]
    Parse --> |是| SetCreds["设置到认证客户端"]
    SetCreds --> CheckAuth{"isAuthenticated()?"}
    CheckAuth --> |否| PromptReauth["提示凭证无效并重新登录"]
    CheckAuth --> |是| CallAPI["调用受保护接口"]
    CallAPI --> Resp{"返回code=200?"}
    Resp --> |是| Success(["处理成功响应"])
    Resp --> |否| ShowErr(["显示错误消息"])
    PromptLogin --> End(["结束"])
    PromptReauth --> End
    Success --> End
    ShowErr --> End
    

图表来源

  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts
  • src/client/cosmoe.ts

章节来源

  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts
  • src/client/cosmoe.ts

会话超时与自动登出策略

  • 当前实现:系统未对凭证设置过期时间或定期轮询校验,KV 中仅保存 user_id、token 与 timestamp
  • 建议策略:
    • 在 KV 值中增加 expires_in 或 refresh_token 字段,结合定时任务定期刷新
    • 在处理器中增加对 timestamp 的校验,超过阈值则提示重新登录
    • 结合外部 API 的 Token 有效期,在调用失败时自动触发刷新或提示重新登录

[本节为通用建议,无需特定文件引用]

认证中间件工作机制

  • 当前架构:未实现全局中间件,各命令处理器在执行前自行检查凭证
  • 实现建议:可在命令注册前安装中间件,拦截命令请求,统一完成凭证读取、校验与上下文注入,减少重复逻辑

[本节为概念性说明,无需特定文件引用]

依赖关系分析

  • 运行时依赖:grammy、@grammyjs/conversations、@grammyjs/storage-cloudflare
  • 配置依赖:wrangler.jsonc 中定义 KV 命名空间绑定与触发器
  • 内部依赖:命令处理器依赖认证客户端;登录/登出/历史/预约处理器共同依赖 KV 存储

    graph LR
    P["package.json"] --> G["grammy"]
    P --> C["@grammyjs/conversations"]
    P --> S["@grammyjs/storage-cloudflare"]
    W["wrangler.jsonc"] --> KV1["COSMOE_CREDENTIALS"]
    W --> KV2["COSMOE_STORAGE"]
    CMD["命令注册(command/index.ts)"] --> H1["login.ts"]
    CMD --> H2["logout.ts"]
    CMD --> H3["history.ts"]
    CMD --> H4["bookEvent.ts"]
    CMD --> H5["events.ts"]
    CMD --> H6["eventDetails.ts"]
    H1 --> CL["cosmoe.ts"]
    H2 --> CL
    H3 --> CL
    H4 --> CL
    H5 --> CL
    H6 --> CL
    CL --> API["Cosmoe API"]
    

图表来源

  • package.json
  • wrangler.jsonc
  • src/command/index.ts
  • src/client/cosmoe.ts

章节来源

  • package.json
  • wrangler.jsonc
  • src/command/index.ts

性能考量

  • KV 读写:对话状态与用户凭证均通过 KV 读写,注意控制键数量与大小,避免频繁写入
  • API 调用:受保护接口需携带 user_id 与 token,建议在处理器内复用认证客户端实例
  • 消息长度:历史查询等场景对消息长度有限制,应做截断处理

[本节提供一般性指导,无需特定文件引用]

故障排除指南

  • 登录失败
    • 检查用户名/密码是否正确
    • 查看认证客户端返回的错误信息
    • 确认外部 API 可达
  • 无法获取 Telegram 用户身份
    • 检查上下文中的 from 字段是否可用
    • 确认命令触发者为真实用户
  • KV 读取失败
    • 检查 KV 命名空间绑定是否正确
    • 确认键格式与权限
  • 凭证无效
    • 重新执行 /login 获取新 Token
    • 检查认证客户端的 isAuthenticated 状态
  • 预约失败
    • 确认时间段仍有余位
    • 检查优惠券是否可用
    • 查看返回的错误消息

章节来源

  • src/command/handlers/login.ts
  • src/command/handlers/logout.ts
  • src/command/handlers/history.ts
  • src/command/handlers/bookEvent.ts

结论

本认证系统通过交互式对话完成登录,使用 KV 存储凭证并在受保护操作前进行状态检查,整体流程清晰、易于维护。建议后续引入中间件统一鉴权、增加凭证过期与自动刷新机制,并完善错误日志与监控,以进一步提升安全性与用户体验。