- 仓储:新增 list_pending_alerts_all / update_alert_escalation(统一读改写 payload, 升级标记独占写入,status/handler_* 列不碰;_parse_alert 补 created_at/handled_at 字符串→datetime 解析,兼容 sqlite 原生 DDL) - escalation_service.scan_and_escalate:扫描判级(普通 4h/24h、AML 1h/4h 短通道)、 幂等闸门(仅升不降)、先持久化再推送、按 (customer_id,level) 降噪合并一次推送、 通知链累积(L1 含 risk_manager / L2 含 compliance)、逐单+任务级审计 - scripts/cron/escalation_scan.py:15min 定时扫描壳(sys.path 引导 + new_trace + JSON 摘要) - 对话线 query_overdue_alerts Tool + 注册表 + 意图词(置于 alert_query 之前)+ summarize 分支 - C5 前置:seed STAFF-31001/31002(risk_manager) + deps 矩阵放行 + chat.py 显式 deny + risk.py 台账全量只读分支;JWT 手册 §5.3/§5.4/§6.1 增补 risk_manager - 测试:conftest 回拨 fixture + test_escalation_service(9) + risk_api/manager(5) + chat deny(1) + chat_tools overdue(2);全量 470 绿(453+17)
588 lines
22 KiB
Markdown
588 lines
22 KiB
Markdown
# 四 Agent 统一 JWT + RBAC 鉴权手册
|
||
|
||
> 需求拆解阶段产物 · 2026-09-05
|
||
> 依据:`业务场景优先级清单` F-01、`数据交互矩阵` §8、`Agent风险与合规约束汇总` G-01
|
||
> 适用范围:**客户财富 / 代理人助手 / 数据分析 / 风控监测** 四个 Agent 及共用底座
|
||
> 目标:**一套身份源、一套 Token 规范、一套 RBAC 模型、一套数据归属校验**,禁止各 Agent 自建鉴权
|
||
|
||
---
|
||
|
||
## 1. 设计原则
|
||
|
||
| 编号 | 原则 | 说明 |
|
||
| --- | --- | --- |
|
||
| P-01 | **统一入口** | 所有 Agent HTTP/WebSocket/API 请求经 **Agent Gateway(或共用 Auth SDK)** 校验,业务服务不自行解析裸 JWT |
|
||
| P-02 | **身份与权限分离** | JWT 只携带 **身份(who)** 与 **角色(roles)**;**数据归属(which customer)** 在数据层二次校验 |
|
||
| P-03 | **最小权限** | 默认拒绝;显式授予 `permission` 才允许读/写/审计 |
|
||
| P-04 | **Agent 边界** | Token 绑定 `agent_type`;客户 Token **不能**调代理人 Agent;内部 Token **不能**冒充客户 |
|
||
| P-05 | **全量留痕** | 鉴权失败、越权尝试必须写 `audit_log` + `input_guard_log`(`illegal_param` / 自定义 `auth_denied`) |
|
||
| P-06 | **与 Core 对齐** | 员工角色、客户 ID、代理人-客户归属以 **Core RBAC + `customer_advisor_rel`** 为准,Agent 库不维护第二套账号体系 |
|
||
|
||
---
|
||
|
||
## 2. 架构总览
|
||
|
||
```text
|
||
┌──────────────┐ ┌──────────────┐ ┌─────────────────────────────┐
|
||
│ C 端 App │ │ 内部工作台 │ │ 交易前钩子 / 批处理 Job │
|
||
│ (客户登录) │ │ (员工 SSO) │ │ (Service Account) │
|
||
└──────┬───────┘ └──────┬───────┘ └──────────────┬──────────────┘
|
||
│ │ │
|
||
└────────────────────┴──────────────────────────────┘
|
||
│
|
||
┌───────────▼───────────┐
|
||
│ Identity Provider │
|
||
│ (Core / 统一 IdP) │
|
||
└───────────┬───────────┘
|
||
│ 签发 JWT
|
||
┌───────────▼───────────┐
|
||
│ Agent Gateway │
|
||
│ ① 验签 ② 过期 ③ RBAC │
|
||
│ ④ 注入 AuthContext │
|
||
└───────────┬───────────┘
|
||
┌─────────────────────┼─────────────────────┐
|
||
│ │ │
|
||
┌──────▼──────┐ ┌───────▼───────┐ ┌───────▼───────┐
|
||
│ 客户财富 │ │ 代理人助手 │ │ 数据分析 │
|
||
│ Agent │ │ Agent │ │ Agent │
|
||
└──────┬──────┘ └───────┬───────┘ └───────┬───────┘
|
||
│ │ │
|
||
└─────────────────────┼─────────────────────┘
|
||
│
|
||
┌───────────▼───────────┐
|
||
│ Data Access Layer │
|
||
│ ⑤ 归属校验 │
|
||
│ ⑥ 画像 L1/L2/L3 RBAC │
|
||
└───────────────────────┘
|
||
```
|
||
|
||
**两层校验(必须都做)**
|
||
|
||
1. **网关层 RBAC**:角色是否允许访问该 Agent、该 API、该 Tool。
|
||
2. **数据层归属**:操作的目标 `customer_id` / `session_id` 是否属于当前操作者(代理人名下、客户本人等)。
|
||
|
||
仅做第 1 层而不做第 2 层,视为 **F-01 未实现**。
|
||
|
||
---
|
||
|
||
## 3. 身份来源与 Token 类型
|
||
|
||
| Token 类型 | 签发方 | 使用者 | `sub` 含义 | 典型 `agent_type` 入口 |
|
||
| --- | --- | --- | --- | --- |
|
||
| `customer` | C 端登录 / Core 客户中心 | 客户本人 | `customer_id` | 仅 **客户财富 Agent** |
|
||
| `staff` | 内部 SSO / Core HR | 员工 | `staff_id` | 代理人 / 分析 / 风控 / 合规 |
|
||
| `service` | 平台密钥 + mTLS | 系统 Job、交易前钩子 | `service_name` | 风控 R-02 钩子、批处理 |
|
||
|
||
> **禁止**:用 `staff` Token 访问客户 Agent 接口冒充客户;用 `customer` Token 访问内部 Agent。
|
||
|
||
---
|
||
|
||
## 4. JWT 规范(四 Agent 共用)
|
||
|
||
### 4.1 Header
|
||
|
||
```json
|
||
{
|
||
"alg": "RS256",
|
||
"typ": "JWT",
|
||
"kid": "2026-09-key-1"
|
||
}
|
||
```
|
||
|
||
- 算法:**RS256**(公钥网关校验,私钥仅 IdP 持有)。
|
||
- 不支持 `none`、HS256 对称密钥(除非开发环境显式开关)。
|
||
|
||
### 4.2 Payload 标准 Claims
|
||
|
||
| Claim | 必填 | 类型 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `iss` | 是 | string | 签发者,如 `https://idp.jinrong.internal` |
|
||
| `sub` | 是 | string | 主体 ID:`customer_id` 或 `staff_id` 或 `service_name` |
|
||
| `aud` | 是 | string[] | 受众,如 `["agent-gateway"]` |
|
||
| `exp` | 是 | number | 过期时间(Unix 秒) |
|
||
| `iat` | 是 | number | 签发时间 |
|
||
| `jti` | 是 | string | Token 唯一 ID,便于吊销与审计 |
|
||
| `token_type` | 是 | enum | `customer` \| `staff` \| `service` |
|
||
| `roles` | 是 | string[] | RBAC 角色,见 §5 |
|
||
| `permissions` | 否 | string[] | 细粒度权限(可选;推荐员工 Token 携带) |
|
||
| `tenant_id` | 是 | string | 租户/法人主体,多法人场景隔离 |
|
||
| `customer_id` | 条件 | string | **仅 `token_type=customer`**:必须等于 `sub` |
|
||
| `advisor_id` | 条件 | string | **代理人角色**:当前员工 ID,等于 `sub` |
|
||
| `session_hint` | 否 | string | 可选,绑定 C 端设备/session,防 Token 盗用 |
|
||
|
||
### 4.3 员工 Token 示例
|
||
|
||
```json
|
||
{
|
||
"iss": "https://idp.jinrong.internal",
|
||
"sub": "STAFF-10086",
|
||
"aud": ["agent-gateway"],
|
||
"exp": 1735689600,
|
||
"iat": 1735686000,
|
||
"jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
|
||
"token_type": "staff",
|
||
"tenant_id": "TENANT-001",
|
||
"roles": ["advisor"],
|
||
"permissions": [
|
||
"agent:advisor:chat",
|
||
"profile:l1:read",
|
||
"profile:l2:write",
|
||
"profile:l3:read",
|
||
"core:holding:read"
|
||
],
|
||
"advisor_id": "STAFF-10086"
|
||
}
|
||
```
|
||
|
||
### 4.4 客户 Token 示例
|
||
|
||
```json
|
||
{
|
||
"iss": "https://idp.jinrong.internal",
|
||
"sub": "CUST-9527",
|
||
"aud": ["agent-gateway"],
|
||
"exp": 1735689600,
|
||
"iat": 1735686000,
|
||
"jti": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
|
||
"token_type": "customer",
|
||
"tenant_id": "TENANT-001",
|
||
"roles": ["customer"],
|
||
"permissions": [
|
||
"agent:customer:chat",
|
||
"profile:l1:read",
|
||
"profile:l1:write",
|
||
"core:holding:read:self"
|
||
],
|
||
"customer_id": "CUST-9527"
|
||
}
|
||
```
|
||
|
||
### 4.5 服务账号 Token(交易前适当性 R-02)
|
||
|
||
```json
|
||
{
|
||
"token_type": "service",
|
||
"sub": "svc-trade-suitability",
|
||
"roles": ["service_risk"],
|
||
"permissions": [
|
||
"agent:risk:suitability_check",
|
||
"profile:l1:read",
|
||
"profile:l2:read",
|
||
"audit:write"
|
||
]
|
||
}
|
||
```
|
||
|
||
- 有效期建议 **≤ 5 分钟**,且绑定调用方 IP / mTLS 证书。
|
||
|
||
### 4.6 请求头约定
|
||
|
||
| Header | 必填 | 说明 |
|
||
| --- | --- | --- |
|
||
| `Authorization` | 是 | `Bearer <JWT>` |
|
||
| `X-Trace-Id` | 是 | 全链路 ID;网关可补发 |
|
||
| `X-Agent-Type` | 是 | `customer` \| `advisor` \| `analyst` \| `risk` |
|
||
| `X-Request-Id` | 否 | 幂等 / 重试 |
|
||
|
||
网关校验 **`X-Agent-Type` 与 Token 允许访问的 Agent 列表一致**,不一致直接 403。
|
||
|
||
---
|
||
|
||
## 5. RBAC 角色与权限模型
|
||
|
||
### 5.1 角色定义(`roles`)
|
||
|
||
| 角色代码 | 中文名 | 可进入 Agent | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `customer` | 客户本人 | 客户财富 | 仅本人数据 |
|
||
| `advisor` | 理财代理人 | 代理人助手 | 名下客户 |
|
||
| `analyst` | 数据分析员 | 数据分析 | 内部只读 + 聚合 |
|
||
| `risk_officer` | 风控专员 | 风控监测 | 预警处置、L3 写 |
|
||
| `compliance` | 合规专员 | 代理人(审计台)、共用审计 API | **只读审计**,不看客户业务对话内容除非授权 |
|
||
| `ops` | 运营 | 代理人(A-08 统计) | 仅脱敏聚合 |
|
||
| `service_risk` | 风控服务账号 | 风控 API | 交易前钩子,无人机交互 |
|
||
|
||
一人可多角色,如 `["advisor", "compliance"]`;权限取 **并集**,数据归属仍按 **最窄范围** 校验。
|
||
|
||
### 5.2 权限命名规范
|
||
|
||
```text
|
||
{scope}:{resource}:{action}[:{qualifier}]
|
||
```
|
||
|
||
| 前缀 scope | 示例 | 含义 |
|
||
| --- | --- | --- |
|
||
| `agent` | `agent:customer:chat` | 访问某 Agent 的对话能力 |
|
||
| `profile` | `profile:l2:write` | 画像 L1/L2/L3 |
|
||
| `core` | `core:holding:read` | Core 只读业务数据 |
|
||
| `risk` | `risk:alert:write` | 预警写入 |
|
||
| `audit` | `audit:read:all` | 合规审计读 |
|
||
| `sql` | `sql:execute:readonly` | 分析 Agent 只读 SQL |
|
||
|
||
### 5.3 角色 → 默认权限包
|
||
|
||
| 角色 | 默认 permissions(摘要) |
|
||
| --- | --- |
|
||
| `customer` | `agent:customer:chat`, `profile:l1:read`, `profile:l1:write`, `core:*:read:self` |
|
||
| `advisor` | `agent:advisor:chat`, `profile:l1:read`, `profile:l2:read`, `profile:l2:write`, `profile:l3:read`, `core:*:read:assigned` |
|
||
| `analyst` | `agent:analyst:chat`, `profile:l1:read`, `profile:l2:read`, `profile:l3:read`, `core:*:read:scoped`, `sql:execute:readonly`, `risk:alert:read` |
|
||
| `risk_officer` | `agent:risk:chat`, `profile:l1:read`, `profile:l2:read`, `profile:l3:read`, `profile:l3:write`, `risk:alert:write`, `risk:suitability:write`, `core:*:read:all` |
|
||
| `risk_manager` | `risk:alert:read`, `core:*:read:all` | 风控经理:HTTP 台账全量只读,**无处置权、对话线经 chat 层显式拒绝**(PRD 4A.1 / C5 前置) |
|
||
| `compliance` | `audit:read:all`, `agent:advisor:audit`, `compliance:hit:read` |
|
||
| `ops` | `agent:advisor:stats`, `audit:read:aggregated` |
|
||
| `service_risk` | `agent:risk:suitability_check`, `risk:suitability:write`, `profile:l1:read`, `profile:l2:read`, `audit:write` |
|
||
|
||
> 实际权限以 Core RBAC 同步为准;上表为 **Agent 平台最低基线**。
|
||
|
||
### 5.4 Agent × 角色 准入矩阵
|
||
|
||
| Agent (`X-Agent-Type`) | 允许 `token_type` | 允许 `roles` |
|
||
| --- | --- | --- |
|
||
| `customer` | `customer` | `customer` |
|
||
| `advisor` | `staff` | `advisor`, `compliance`, `ops` |
|
||
| `analyst` | `staff` | `analyst`, `compliance` |
|
||
| `risk` | `staff`, `service` | `risk_officer`, `service_risk`, `risk_manager` |
|
||
|
||
---
|
||
|
||
## 6. 数据层归属校验(第二层)
|
||
|
||
JWT 通过后,**任何涉及 `customer_id` 的读写** 必须执行归属校验。
|
||
|
||
### 6.1 校验规则表
|
||
|
||
| 操作者角色 | 目标资源 | 校验逻辑 | 失败错误码 |
|
||
| --- | --- | --- | --- |
|
||
| `customer` | 任意含 `customer_id` 的资源 | `token.customer_id == resource.customer_id` | `AUTH_403_NOT_OWNER` |
|
||
| `advisor` | 客户画像 / 持仓 / 会话 | `customer_advisor_rel` 存在 `advisor_id=sub AND customer_id=? AND rel_status=active` | `AUTH_403_NOT_ASSIGNED` |
|
||
| `analyst` | 客户级明细 | 走 **数据权限目录**;无明细权限则 **仅允许聚合 SQL**(`COUNT`/`GROUP BY`,结果 ≥ k-匿名阈值) | `AUTH_403_SCOPE` |
|
||
| `risk_officer` | 全量客户 | 允许读全部;写仅限 L3 / 预警 / 适当性 | — |
|
||
| `risk_manager` | 全量客户 | **仅读**预警台账(`risk:alert:read`,无 `risk:alert:write`);对话线(chat)经 `app/api/chat.py` 显式 `deny(AUTH_403_ROLE)` 拒绝,强制走 HTTP 台账 | — |
|
||
| `compliance` | 审计 / 会话 | 可跨客户读 **审计类表**;读 `agent_message` 需 `audit:read:content` 额外权限 | `AUTH_403_AUDIT_SCOPE` |
|
||
| 跨 Agent 读会话 | `agent_session` | 角色只能读 **同 agent_type** 会话;合规除外 | `AUTH_403_SESSION_AGENT` |
|
||
|
||
### 6.2 归属校验伪代码(共用 SDK)
|
||
|
||
```python
|
||
def assert_customer_access(ctx: AuthContext, customer_id: str, action: str):
|
||
if ctx.token_type == "customer":
|
||
if ctx.sub != customer_id:
|
||
audit_denied(ctx, "AUTH_403_NOT_OWNER", customer_id)
|
||
raise Forbidden(...)
|
||
|
||
elif "advisor" in ctx.roles:
|
||
if not rel_exists(ctx.sub, customer_id, status="active"):
|
||
audit_denied(ctx, "AUTH_403_NOT_ASSIGNED", customer_id)
|
||
raise Forbidden(...)
|
||
|
||
elif "analyst" in ctx.roles:
|
||
if action == "detail" and not ctx.has_perm("core:customer:read:detail"):
|
||
audit_denied(ctx, "AUTH_403_SCOPE", customer_id)
|
||
raise Forbidden(...)
|
||
# aggregate 走 SQL 网关白名单
|
||
|
||
elif "risk_officer" in ctx.roles:
|
||
return # 读全开;写由 permission 再判
|
||
|
||
else:
|
||
audit_denied(ctx, "AUTH_403_ROLE", customer_id)
|
||
raise Forbidden(...)
|
||
```
|
||
|
||
### 6.3 画像 L1 / L2 / L3 RBAC(与数据矩阵对齐)
|
||
|
||
| 资源 | customer | advisor | analyst | risk_officer | compliance |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| L1 读 | 本人 | 名下客户 | 脱敏/聚合 | 全部 | 审计 |
|
||
| L1 写 | 本人 | — | — | — | — |
|
||
| L2 读 | — | 名下客户 | 脱敏/聚合 | 全部 | 审计 |
|
||
| L2 写 | — | 名下客户 | — | — | — |
|
||
| L3 读 | — | 名下客户 | 脱敏/聚合 | 全部 | 审计 |
|
||
| L3 写 | — | — | — | ✓ | — |
|
||
|
||
**客户永远不可见 L2/L3**(API 层直接 404,避免泄露存在性)。
|
||
|
||
---
|
||
|
||
## 7. 各 Agent 鉴权要点
|
||
|
||
### 7.1 客户财富 Agent
|
||
|
||
| 检查点 | 规则 |
|
||
| --- | --- |
|
||
| 入口 | 仅 `token_type=customer` |
|
||
| 会话 | `agent_session.actor_id` 必须 = `customer_id` |
|
||
| Core 只读 | 持仓/流水 API 自动注入 `customer_id=ctx.sub` |
|
||
| Tool | 禁止传入他人 `customer_id`;传入则 `AUTH_403` + 审计 |
|
||
| 画像 | 只写 L1;读 L3 禁止 |
|
||
| 适当性 | 只读 `risk_suitability_log` 本人记录 |
|
||
|
||
### 7.2 代理人助手 Agent
|
||
|
||
| 检查点 | 规则 |
|
||
| --- | --- |
|
||
| 入口 | `staff` + `advisor`/`compliance`/`ops` |
|
||
| 查客户 | 请求体/Tool 中 `customer_id` **必过** `customer_advisor_rel` |
|
||
| 合规台 A-05 | `compliance` 角色 + `audit:read:all`;可按 `advisor_id` 过滤,不能改数据 |
|
||
| 草稿 A-03 | `advisor_draft.advisor_id` 必须 = `ctx.sub` |
|
||
| 外发 | 无 `message:send:customer` 权限;系统级禁止代理人 Agent 直发客户 |
|
||
|
||
### 7.3 数据分析 Agent
|
||
|
||
| 检查点 | 规则 |
|
||
| --- | --- |
|
||
| 入口 | `staff` + `analyst` |
|
||
| SQL | 仅 `SELECT`;SQL 网关白名单 schema;禁止 `INSERT/UPDATE/DELETE` |
|
||
| 明细 vs 聚合 | 无明细权限时,拦截含 `customer_id` 单行输出的查询 |
|
||
| 画像 | L1/L2/L3 **只读**;写请求一律 403 |
|
||
| 预警 | `risk_alert` 只读;无 `risk:alert:handle` |
|
||
| 留痕 | 每次查询写 `analytics_query_log`,含 `staff_id=ctx.sub` |
|
||
|
||
### 7.4 风控监测 Agent
|
||
|
||
| 检查点 | 规则 |
|
||
| --- | --- |
|
||
| 人机入口 | `staff` + `risk_officer` |
|
||
| 交易钩子 R-02 | `service` + `service_risk` + mTLS;不创建会话 |
|
||
| 写权限 | 可写 `risk_alert`, `customer_profile_l3`, `risk_suitability_log` |
|
||
| 读权限 | L0/L1/L2 全读;**不可写** L1/L2 |
|
||
| 阻断 | 仅 R-02 API 可返回 `blocked`;须写 `audit_log` + `risk_suitability_log` |
|
||
|
||
---
|
||
|
||
## 8. Tool / API 统一鉴权流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Client
|
||
participant Gateway
|
||
participant Agent
|
||
participant AuthSDK
|
||
participant DB
|
||
|
||
Client->>Gateway: Request + JWT + X-Agent-Type
|
||
Gateway->>Gateway: 验签 / exp / aud / token_type
|
||
Gateway->>Gateway: RBAC: role ∈ allowed?
|
||
Gateway->>Agent: AuthContext (sub, roles, perms, trace_id)
|
||
Agent->>AuthSDK: check_resource(customer_id, action)
|
||
AuthSDK->>DB: customer_advisor_rel / self check
|
||
alt 通过
|
||
AuthSDK-->>Agent: OK
|
||
Agent->>DB: 业务读写
|
||
else 拒绝
|
||
AuthSDK->>DB: audit_log (auth_denied)
|
||
AuthSDK-->>Agent: Forbidden
|
||
Agent-->>Client: 403 + error_code
|
||
end
|
||
```
|
||
|
||
### 8.1 AuthContext(注入给各 Agent 服务)
|
||
|
||
```typescript
|
||
interface AuthContext {
|
||
traceId: string;
|
||
tokenType: 'customer' | 'staff' | 'service';
|
||
subjectId: string; // sub
|
||
roles: string[];
|
||
permissions: string[];
|
||
tenantId: string;
|
||
customerId?: string; // token_type=customer
|
||
advisorId?: string; // 代理人
|
||
agentType: 'customer' | 'advisor' | 'analyst' | 'risk';
|
||
jti: string;
|
||
}
|
||
```
|
||
|
||
### 8.2 共用 SDK 能力清单
|
||
|
||
| 模块 | 方法 | 说明 |
|
||
| --- | --- | --- |
|
||
| `JwtVerifier` | `verify(token)` | 验签、解析、吊销列表检查 |
|
||
| `RbacGuard` | `can(ctx, permission)` | 权限判断 |
|
||
| `OwnershipGuard` | `assertCustomer(ctx, customerId)` | 归属校验 |
|
||
| `ProfileGuard` | `assertProfileAccess(ctx, layer, op)` | L1/L2/L3 |
|
||
| `SessionGuard` | `assertSession(ctx, sessionId)` | 会话归属 + agent_type |
|
||
| `AuditService` | `logDenied(ctx, code, detail)` | 越权审计 |
|
||
|
||
---
|
||
|
||
## 9. 会话与 Trace 绑定
|
||
|
||
创建 `agent_session` 时:
|
||
|
||
| 字段 | 赋值规则 |
|
||
| --- | --- |
|
||
| `agent_type` | = `X-Agent-Type` |
|
||
| `actor_id` | = `ctx.sub` |
|
||
| `actor_role` | = 主角色(`customer`/`advisor`/…) |
|
||
| `customer_id` | 客户 Agent:`ctx.customer_id`;代理人 Agent:请求指定且通过归属校验 |
|
||
| `advisor_id` | 代理人 Agent:`ctx.advisor_id` |
|
||
| `trace_id` | = `X-Trace-Id` |
|
||
|
||
后续同会话所有 `agent_message` / `agent_tool_call` / `audit_log` 必须带同一 `trace_id`。
|
||
|
||
**禁止**:使用他人 `session_id` 续聊(`SessionGuard` 校验 `actor_id`)。
|
||
|
||
---
|
||
|
||
## 10. 错误码与 HTTP 状态
|
||
|
||
| HTTP | error_code | 含义 | 是否写审计 |
|
||
| --- | --- | --- | --- |
|
||
| 401 | `AUTH_401_INVALID_TOKEN` | 签名无效 / 过期 / 格式错误 | 是 |
|
||
| 401 | `AUTH_401_REVOKED` | jti 已吊销 | 是 |
|
||
| 403 | `AUTH_403_ROLE` | 角色不允许进该 Agent | 是 |
|
||
| 403 | `AUTH_403_PERMISSION` | 缺少 permission | 是 |
|
||
| 403 | `AUTH_403_NOT_OWNER` | 客户访问非本人数据 | 是 |
|
||
| 403 | `AUTH_403_NOT_ASSIGNED` | 代理人访问非名下客户 | 是 |
|
||
| 403 | `AUTH_403_SCOPE` | 分析员超数据范围 | 是 |
|
||
| 403 | `AUTH_403_SESSION_AGENT` | 跨 Agent 读会话 | 是 |
|
||
| 403 | `AUTH_403_AGENT_MISMATCH` | X-Agent-Type 与 Token 不符 | 是 |
|
||
|
||
响应体统一:
|
||
|
||
```json
|
||
{
|
||
"error_code": "AUTH_403_NOT_ASSIGNED",
|
||
"message": "无权访问该客户数据",
|
||
"trace_id": "trace-xxx",
|
||
"request_id": "req-xxx"
|
||
}
|
||
```
|
||
|
||
**禁止**在 403 响应中返回「该客户是否存在」等可被枚举的信息(客户 Agent 对外统一话术)。
|
||
|
||
---
|
||
|
||
## 11. 安全补充要求
|
||
|
||
| 项 | 要求 |
|
||
| --- | --- |
|
||
| Token 有效期 | 客户 Access Token ≤ 2h,Refresh 走 IdP;员工 ≤ 8h |
|
||
| 吊销 | 支持 jti 黑名单(Redis `auth:revoked:{jti}`) |
|
||
| 限流 | 复用 Redis `guard:rate:{actor_id}:{agent}` |
|
||
| 高危越权 | 同一 `actor_id` 10 分钟内 ≥ 5 次 `AUTH_403_*` → 临时 `guard:block` |
|
||
| 日志 | 不在日志打印完整 JWT;只打 `sub`、`jti` 前 8 位 |
|
||
| 开发环境 | 可用 HS256 + 固定 dev secret,**与生产密钥物理隔离** |
|
||
|
||
---
|
||
|
||
## 12. 与现有表结构的关系
|
||
|
||
| 表 | 鉴权用途 |
|
||
| --- | --- |
|
||
| `customer_advisor_rel` | 代理人 **数据归属** 权威来源(Core 同步) |
|
||
| `agent_session` | 会话 `actor_id` / `advisor_id` / `customer_id` 一致性 |
|
||
| `audit_log` | 越权、鉴权失败、`event_type=auth_denied` |
|
||
| `input_guard_log` | 恶意参数、伪造 `customer_id` |
|
||
|
||
---
|
||
|
||
## 13. 验收检查清单(F-01 P0)
|
||
|
||
### 平台 / 网关
|
||
|
||
- [ ] 四个 Agent 共用同一 `JwtVerifier` 与 `RbacGuard`
|
||
- [ ] 错误 Token 一律 401,且不进入业务逻辑
|
||
- [ ] `X-Agent-Type` 与客户/员工 Token 交叉校验生效
|
||
|
||
### 客户财富 Agent
|
||
|
||
- [ ] 客户 A 不能查客户 B 持仓(403 + 审计)
|
||
- [ ] 客户 Token 不能访问 advisor/analyst/risk 路由
|
||
|
||
### 代理人助手 Agent
|
||
|
||
- [ ] 代理人 A 不能查代理人 B 名下客户
|
||
- [ ] 合规角色可审计会话,但不能写 L2 画像
|
||
|
||
### 数据分析 Agent
|
||
|
||
- [ ] 非授权明细 SQL 被拦截
|
||
- [ ] 不能 UPDATE 任何业务表
|
||
|
||
### 风控监测 Agent
|
||
|
||
- [ ] 服务账号可调用 R-02,但不能登录人机聊天台
|
||
- [ ] 风控不能写 L1/L2
|
||
|
||
---
|
||
|
||
## 14. 实施分工建议
|
||
|
||
| 负责方 | 交付物 |
|
||
| --- | --- |
|
||
| **平台组** | Agent Gateway、Auth SDK、`customer_advisor_rel` 同步 Job |
|
||
| **IdP / Core** | 角色权限源、JWT 签发与吊销 API |
|
||
| **各 Agent 组** | 接入 SDK;Tool 入口调用 `OwnershipGuard`;禁止本地 JWT 解析 |
|
||
| **合规** | 验收 §13 用例 + A-05 审计台权限 |
|
||
|
||
---
|
||
|
||
## 15. 关联文档
|
||
|
||
| 文档 | 关系 |
|
||
| --- | --- |
|
||
| [业务场景优先级清单](../../需求拆解/业务场景优先级清单.md) §3.1 F-01 | 需求来源 |
|
||
| [数据交互矩阵](../../需求拆解/数据交互矩阵.md) §8 | 画像 RBAC |
|
||
| [Agent风险与合规约束汇总](../../需求拆解/Agent风险与合规约束汇总.md) G-01 | 全局约束 |
|
||
| [05-多Agent共用底座清单](../项目框架设计/表设计/05-多Agent共用底座清单.md) | 平台 SDK 与 `customer_advisor_rel` |
|
||
| [02-redis-keys.md](../项目框架设计/表设计/02-redis-keys.md) | 限流 / 封禁 Key |
|
||
| [01-技术栈与版本.md](./01-技术栈与版本.md) | FastAPI / Redis / 部署环境 |
|
||
|
||
---
|
||
|
||
## 附录 A:权限全集(实现时可裁剪)
|
||
|
||
```text
|
||
# Agent 入口
|
||
agent:customer:chat
|
||
agent:advisor:chat
|
||
agent:analyst:chat
|
||
agent:risk:chat
|
||
agent:risk:suitability_check
|
||
agent:advisor:audit
|
||
agent:advisor:stats
|
||
|
||
# 画像
|
||
profile:l1:read
|
||
profile:l1:write
|
||
profile:l2:read
|
||
profile:l2:write
|
||
profile:l3:read
|
||
profile:l3:write
|
||
|
||
# Core 只读
|
||
core:holding:read:self
|
||
core:holding:read:assigned
|
||
core:holding:read:scoped
|
||
core:holding:read:all
|
||
core:customer:read:detail
|
||
|
||
# 风控
|
||
risk:alert:read
|
||
risk:alert:write
|
||
risk:suitability:write
|
||
risk:suitability:read
|
||
|
||
# 分析与审计
|
||
sql:execute:readonly
|
||
audit:read:all
|
||
audit:read:aggregated
|
||
audit:read:content
|
||
audit:write
|
||
compliance:hit:read
|
||
```
|
||
|
||
## 附录 B:Core RBAC 同步建议
|
||
|
||
| Core 角色 | 映射 Agent roles | 同步频率 |
|
||
| --- | --- | --- |
|
||
| 理财师 | `advisor` | 实时 / 5min |
|
||
| 风控经理 | `risk_officer` | 实时 |
|
||
| 合规 | `compliance` | 实时 |
|
||
| 数据分析师 | `analyst` | 实时 |
|
||
| 运营 | `ops` | 日批 |
|
||
|
||
员工离职 / 转岗:Core 禁用账号 → IdP 吊销 Refresh Token → Redis 黑名单现有 Access Token `jti`。
|