# 路线 05:从登录追踪到租户隔离、权限检查与后台复验
## 学习场景
客户登录后查询自己的工单;同一身份提交 Agent 任务,工作进程在稍后执行时重新检查会话和权限;平台员工通过独立入口访问平台功能。沿三个场景观察凭据如何建立、身份如何验证、资源范围如何限制。
图中的后端路径均相对 `src/zhifu/`。连续箭头表示直接函数调用;标注“框架调用”的箭头表示 FastAPI 或 LangGraph 调用已注册函数。间断箭头表示 HTTP、持久数据或框架注册连接,箭头说明具体含义。数据节点使用类名,函数节点使用源码中的函数名。返回[路线目录](README.md)。
## 图 1:登录与一次工单查询
登录成功产生有效期为 3600 秒的 opaque Bearer token,数据库保存 token 的 SHA-256 摘要。客户查询工单时,服务端重新验证会话、租户与账户状态,然后检查店铺权限及工单客户归属。
```mermaid
flowchart TB
CLIENT["客户端
保存登录响应中的 token"]
LOGIN["modules/identity/auth.py
login
限流、密码验证与会话创建"]
SESSION[("modules/identity/access_models.py
AccessSession
token_hash、有效期与撤销状态")]
GET["modules/after_sales/api.py
get_ticket
建立查询事务"]
AUTH["modules/identity/auth.py
authenticate
读取 Bearer token 并计算摘要"]
HASH["modules/identity/auth.py
authenticate_hash
复查会话、租户和账户"]
TENANT[("modules/identity/models.py
Tenant
租户状态")]
PRINCIPAL[("modules/identity/access_models.py
Principal
账户归属、类型与状态")]
FIND["modules/after_sales/service.py
AfterSalesService.find
按 tenant_id 与资源 id 查询"]
ACCESS["modules/after_sales/service.py
AfterSalesService.access
检查客户本人资源"]
STORE["modules/identity/auth.py
authorize_store
检查店铺状态与指定权限"]
GRANT[("modules/identity/access_models.py
StoreGrant
租户、账户、店铺、permission")]
VIEW["modules/after_sales/service.py
AfterSalesService.ticket_view
返回授权范围内工单"]
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`。
### 图 1 源码导航
- [login](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/auth.py:104)、[AccessSession](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/access_models.py:28):核对 token 返回值与数据库存储字段。
- [get_ticket](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/api.py:202)、[authenticate](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/auth.py:50)、[authenticate_hash](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/auth.py:66):追踪请求身份进入 `AuthContext` 的过程。
- [Tenant](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/models.py:10)、[Principal](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/access_models.py:10)、[StoreGrant](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/access_models.py:42):核对租户外键、账户类型和权限作用域。
- [find](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:74)、[access](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:82)、[authorize_store](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/auth.py:90)、[ticket_view](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/after_sales/service.py:157)。
- [logout](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/identity/auth.py:196):同样调用 `authenticate`,然后将当前 `AccessSession.revoked` 设为 `True`。
## 图 2:后台 Agent 运行重新验证身份
业务 API 在 `queue_run` 中保存请求身份的 `token_hash`。后台工作进程领取数据库中的任务,再调用编译后的 LangGraph。`build_graph` 注册节点,`graph.ainvoke` 触发节点执行;图中分别标记注册关系和框架调用关系。
```mermaid
flowchart TB
API["modules/agent_runtime/api.py
enqueue
认证、店铺授权与幂等检查"]
QUEUE["modules/agent_runtime/service.py
queue_run
保存身份与运行配置"]
RUN[("modules/agent_runtime/models.py
AgentRun
身份摘要、状态与租约")]
WORKER["modules/agent_runtime/worker.py
execute_one
领取并执行一次运行"]
CLAIM["modules/agent_runtime/worker.py
claim
领取任务并增加 lease_token"]
BUILD["modules/agent_runtime/runtime.py
build_graph
注册节点并 compile"]
INVOKE["modules/agent_runtime/worker.py
execute_one.invoke
调用 graph.ainvoke"]
LOAD["modules/agent_runtime/runtime.py
build_graph.load
读取持久 next_node"]
MODEL["modules/agent_runtime/runtime.py
build_graph.model
模型请求前后复验"]
LOCKED["modules/agent_runtime/runtime.py
locked_run
身份、控制权、租约与期限检查"]
AUTH["modules/identity/auth.py
authenticate_hash
重新验证持久会话"]
STORE["modules/identity/auth.py
authorize_store
重新检查 orders:read"]
ACCESS[("modules/identity/access_models.py
AccessSession / StoreGrant
当前会话与权限记录")]
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` 记录到当前运行;它只在自己仍持有对应租约时修改运行状态。
### 图 2 源码导航
- [enqueue](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/api.py:60)、[queue_run](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/service.py:20)、[AgentRun](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/models.py:10):核对 `tenant_id`、`actor_id`、`store_id`、`token_hash` 的保存来源。
- [execute_one](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/worker.py:137)、[claim](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/worker.py:28)、[execute_one.invoke](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/worker.py:165)。
- [build_graph](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:244)、[load](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:247)、[model](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:284)、[节点注册与条件路由](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:412)。
- [locked_run](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:47)、[工具子图 execute](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:96)、[followup](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/agent_runtime/runtime.py:252)。
- [履约 prepare](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/integrations/fulfillment.py:85) 直接读取 `Tenant`、`Store`、`Principal`、`StoreGrant`,重新检查审批员工的当前权限。该业务事件路径见[路线 03 的图 2](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/docs/reading-routes/03-after-sales.md)。
## 图 3:平台员工的独立身份
平台登录入口使用 `PlatformAccount` 和 `PlatformSession`。平台请求的 `authenticate` 在平台会话表中寻找凭据,并检查账户启用状态及 `platform:manage`。
```mermaid
flowchart TB
HTTP["FastAPI
/openapi/v1/platform/auth"]
LOGIN["modules/platform/auth.py
login
平台账户密码验证与限流"]
ACCOUNT[("modules/platform/models.py
PlatformAccount
平台账户与 permissions")]
SESSION[("modules/platform/models.py
PlatformSession
平台 token 摘要与有效期")]
ME["modules/platform/auth.py
me
查询当前平台身份"]
LOGOUT["modules/platform/auth.py
logout
撤销当前平台会话"]
AUTH["modules/platform/auth.py
authenticate
检查平台会话与 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`,退出操作仅撤销本次平台会话。
### 图 3 源码导航
- [平台 login](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/platform/auth.py:44)、[平台 authenticate](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/platform/auth.py:20)。
- [PlatformAccount](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/platform/models.py:10)、[PlatformSession](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/platform/models.py:23)。
- [平台 me](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/platform/auth.py:91)、[平台 logout](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/platform/auth.py:106)。
## 关键数据与状态
- `Principal.kind` 包含 `CUSTOMER`、`STAFF`、`SERVICE`;客服、主管和商家管理员的具体职责通过店铺权限表达。可阅读 [ROLE_PERMISSIONS 与 management_access](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/src/zhifu/modules/merchant/service.py:22) 理解管理入口的身份检查。
- `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` 检查。
## 阅读检查问题
1. 查询工单时,租户范围、店铺权限和客户本人归属分别在哪个函数检查?这些检查需要哪些字段?
2. Agent 任务已经进入队列之后,客户退出登录或客服接管会话,哪些检查会阻止工作进程继续保存响应?模型用量为什么另行保存?
3. 平台员工 token 在哪张表中验证?撤销平台会话与撤销客户会话分别修改什么记录?
## 相关测试阅读入口
本路线只阅读测试代码,未执行下列用例。
- [test_confirmation_approval_and_isolation](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_after_sales.py:298):客户确认、审批身份与租户访问范围。
- [test_permission_revocation_and_logout](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_after_sales.py:392):撤销店铺权限与退出当前会话后的响应。
- [test_tool_cannot_read_another_customer_order](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_agent_runtime.py:93)、[test_revoked_session_stops_tool_execution](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_agent_runtime.py:106):后台工具访问客户订单及会话撤销检查。
- [test_revoked_approval_permission_prevents_external_operation](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_fulfillment.py:302):履约准备阶段读取当前审批权限。
- [test_platform_identity_quota_and_audit](G:/leon/miaoa/workspace/shuh-ai/zhifu-platform/tests/test_platform_management.py:92):平台独立会话检查及租户停用后的身份验证。