客户登录后查询自己的工单;同一身份提交 Agent 任务,工作进程在稍后执行时重新检查会话和权限;平台员工通过独立入口访问平台功能。沿三个场景观察凭据如何建立、身份如何验证、资源范围如何限制。
图中的后端路径均相对 src/zhifu/。连续箭头表示直接函数调用;标注“框架调用”的箭头表示 FastAPI 或 LangGraph 调用已注册函数。间断箭头表示 HTTP、持久数据或框架注册连接,箭头说明具体含义。数据节点使用类名,函数节点使用源码中的函数名。返回路线目录。
登录成功产生有效期为 3600 秒的 opaque Bearer token,数据库保存 token 的 SHA-256 摘要。客户查询工单时,服务端重新验证会话、租户与账户状态,然后检查店铺权限及工单客户归属。
flowchart TB
CLIENT["客户端<br/>保存登录响应中的 token"]
LOGIN["modules/identity/auth.py<br/>login<br/>限流、密码验证与会话创建"]
SESSION[("modules/identity/access_models.py<br/>AccessSession<br/>token_hash、有效期与撤销状态")]
GET["modules/after_sales/api.py<br/>get_ticket<br/>建立查询事务"]
AUTH["modules/identity/auth.py<br/>authenticate<br/>读取 Bearer token 并计算摘要"]
HASH["modules/identity/auth.py<br/>authenticate_hash<br/>复查会话、租户和账户"]
TENANT[("modules/identity/models.py<br/>Tenant<br/>租户状态")]
PRINCIPAL[("modules/identity/access_models.py<br/>Principal<br/>账户归属、类型与状态")]
FIND["modules/after_sales/service.py<br/>AfterSalesService.find<br/>按 tenant_id 与资源 id 查询"]
ACCESS["modules/after_sales/service.py<br/>AfterSalesService.access<br/>检查客户本人资源"]
STORE["modules/identity/auth.py<br/>authorize_store<br/>检查店铺状态与指定权限"]
GRANT[("modules/identity/access_models.py<br/>StoreGrant<br/>租户、账户、店铺、permission")]
VIEW["modules/after_sales/service.py<br/>AfterSalesService.ticket_view<br/>返回授权范围内工单"]
CLIENT -.->|HTTP POST /openapi/v1/auth/sessions;FastAPI 分发| LOGIN
LOGIN -.->|事务保存 token 摘要| SESSION
LOGIN -.->|HTTP 响应返回 token| CLIENT
CLIENT -.->|"HTTP GET /openapi/v1/tickets/{ticket_id};FastAPI 分发"| GET
GET --> AUTH
AUTH --> HASH
SESSION -.->|读取与重新刷新| HASH
TENANT -.->|锁定并检查 ACTIVE| HASH
PRINCIPAL -.->|锁定并检查 ACTIVE| HASH
GET -->|认证成功后| FIND
GET -->|取得工单后| ACCESS
ACCESS --> STORE
GRANT -.->|按四个字段读取权限记录| STORE
GET -->|全部检查通过后| VIEW
login 按账户和客户端地址分别限制每 60 秒 10 次、60 次,失败请求也计数。密码验证通过后,它重新读取账户状态和密码摘要,再登记会话。authenticate_hash 对账户记录加锁并重新刷新会话状态,使当前操作与权限撤销具有明确的事务顺序。
find 将 auth.tenant_id 加入查询;authorize_store 检查店铺属于当前租户、处于 ACTIVE,且存在指定 StoreGrant;access 对 CUSTOMER 继续检查 subject_id == actor_id。无权访问业务资源时返回 RESOURCE_NOT_FOUND。身份或会话失效时返回 UNAUTHENTICATED。
AuthContext 的过程。authenticate,然后将当前 AccessSession.revoked 设为 True。业务 API 在 queue_run 中保存请求身份的 token_hash。后台工作进程领取数据库中的任务,再调用编译后的 LangGraph。build_graph 注册节点,graph.ainvoke 触发节点执行;图中分别标记注册关系和框架调用关系。
flowchart TB
API["modules/agent_runtime/api.py<br/>enqueue<br/>认证、店铺授权与幂等检查"]
QUEUE["modules/agent_runtime/service.py<br/>queue_run<br/>保存身份与运行配置"]
RUN[("modules/agent_runtime/models.py<br/>AgentRun<br/>身份摘要、状态与租约")]
WORKER["modules/agent_runtime/worker.py<br/>execute_one<br/>领取并执行一次运行"]
CLAIM["modules/agent_runtime/worker.py<br/>claim<br/>领取任务并增加 lease_token"]
BUILD["modules/agent_runtime/runtime.py<br/>build_graph<br/>注册节点并 compile"]
INVOKE["modules/agent_runtime/worker.py<br/>execute_one.invoke<br/>调用 graph.ainvoke"]
LOAD["modules/agent_runtime/runtime.py<br/>build_graph.load<br/>读取持久 next_node"]
MODEL["modules/agent_runtime/runtime.py<br/>build_graph.model<br/>模型请求前后复验"]
LOCKED["modules/agent_runtime/runtime.py<br/>locked_run<br/>身份、控制权、租约与期限检查"]
AUTH["modules/identity/auth.py<br/>authenticate_hash<br/>重新验证持久会话"]
STORE["modules/identity/auth.py<br/>authorize_store<br/>重新检查 orders:read"]
ACCESS[("modules/identity/access_models.py<br/>AccessSession / StoreGrant<br/>当前会话与权限记录")]
API --> QUEUE
QUEUE -.->|API 事务提交| RUN
WORKER --> CLAIM
RUN -.->|跨进程数据库领取| CLAIM
WORKER -->|领取成功后| BUILD
WORKER --> INVOKE
BUILD -.->|注册 load 节点| LOAD
BUILD -.->|注册 model 节点| MODEL
INVOKE -->|LangGraph 框架调用 START 后的 load| LOAD
INVOKE -->|LangGraph 框架调用;条件路由 next_node=model| MODEL
LOAD --> LOCKED
MODEL -->|模型请求前及保存响应前| LOCKED
RUN -.->|读取身份摘要并锁定当前运行| LOCKED
LOCKED --> AUTH
LOCKED -->|核对运行身份、控制权和租约后| STORE
ACCESS -.->|读取当前状态| AUTH
ACCESS -.->|读取当前权限| STORE
图中选择 load 和 model 展示复验时机。工具子图的 execute 与 followup 节点也调用 locked_run。locked_run 检查 token 对应身份与 AgentRun 一致;有关联会话时检查 control_owner、control_version 和 active_run_id;随后确认 RUNNING 状态、有效租约、执行期限、图版本及店铺授权。
模型请求结束后先独立保存已经发生的用量,再复验身份与控制权并提交响应。身份失效等异常由 execute_one 记录到当前运行;它只在自己仍持有对应租约时修改运行状态。
tenant_id、actor_id、store_id、token_hash 的保存来源。Tenant、Store、Principal、StoreGrant,重新检查审批员工的当前权限。该业务事件路径见路线 03 的图 2。平台登录入口使用 PlatformAccount 和 PlatformSession。平台请求的 authenticate 在平台会话表中寻找凭据,并检查账户启用状态及 platform:manage。
flowchart TB
HTTP["FastAPI<br/>/openapi/v1/platform/auth"]
LOGIN["modules/platform/auth.py<br/>login<br/>平台账户密码验证与限流"]
ACCOUNT[("modules/platform/models.py<br/>PlatformAccount<br/>平台账户与 permissions")]
SESSION[("modules/platform/models.py<br/>PlatformSession<br/>平台 token 摘要与有效期")]
ME["modules/platform/auth.py<br/>me<br/>查询当前平台身份"]
LOGOUT["modules/platform/auth.py<br/>logout<br/>撤销当前平台会话"]
AUTH["modules/platform/auth.py<br/>authenticate<br/>检查平台会话与 platform:manage"]
HTTP -->|框架调用 POST /sessions| LOGIN
HTTP -->|框架调用 GET /me| ME
HTTP -->|框架调用 DELETE /sessions/current| LOGOUT
ACCOUNT -.->|查询密码摘要并重新核对状态和权限| LOGIN
LOGIN -.->|事务保存独立会话| SESSION
ME --> AUTH
LOGOUT --> AUTH
SESSION -.->|读取并重新刷新| AUTH
ACCOUNT -.->|锁定账户并校验权限| AUTH
LOGOUT -.->|认证成功后设置 revoked=True| SESSION
平台登录成功后同样返回 3600 秒 token。authenticate 通过 PlatformSession.account_id 找到平台账户,锁定账户后再次刷新会话;随后检查当前 permissions 是否包含 platform:manage。me 返回 kind=PLATFORM,退出操作仅撤销本次平台会话。
Principal.kind 包含 CUSTOMER、STAFF、SERVICE;客服、主管和商家管理员的具体职责通过店铺权限表达。可阅读 ROLE_PERMISSIONS 与 management_access 理解管理入口的身份检查。StoreGrant 的四个主键字段共同限定权限。资源读取同时受 tenant_id、店铺权限和客户归属约束。AccessSession 保存 token_hash、tenant_id、principal_id、expires_at、revoked。authenticate_hash 从当前记录构建 AuthContext。ACTIVE。有效 token 对应的账户或租户停用后,后续身份检查返回 401;业务资源授权不足时,相关入口返回 404。AgentRun 保存原始身份摘要和执行控制信息。lease_token 与 lease_until 限制工作进程提交权;关联会话的控制版本限制会话接管后的继续执行。PlatformAccount 和 PlatformSession 管理平台员工身份;平台权限通过 PlatformAccount.permissions 检查。本路线只阅读测试代码,未执行下列用例。