docs(03-02): docs/修改记录.md 顶部追加 Phase 3 条目 (CRED-05 + CRED-06)

- 5 处文件改动汇总: aiapp/views.py + qy_lty/urls.py + common/logging/__init__.py + common/logging/filters.py + qy_lty/settings.py
- 修改类型: 新增
- 修改内容: 客户端 GET 接口明文返回 + AccessTokenMaskFilter 4 正则脱敏 + LOGGING 注册
- 修改原因: Milestone v1.0 收尾 phase, 客户端读取 + 日志防御性兜底
- 跨项目联动: 无 — 客户端给 Unity (LTY_Project / LTY_App_Project_URP) 用, 那两个 repo 各自维护; qy-lty-admin 不消费此接口
- qy-lty-admin/docs/修改记录.md mtime 验证未变, 不写互引
This commit is contained in:
pmc 2026-05-08 10:30:14 +08:00
parent 7a9e511132
commit db4d5cf89d

View File

@ -23,6 +23,29 @@
<!-- 新的修改记录添加在此处下方,最新的在最前面 -->
### [2026-05-08] Phase 3 — 客户端凭据槽位 GET 接口 + 阿里云日志 access_token 脱敏
配套 Phase[.planning/phases/03-client-and-log-mask/](.planning/phases/03-client-and-log-mask/)
覆盖需求CRED-05 + CRED-06
设计参考1:1 复刻 `aiapp.views.CredentialSlotAdminView` 的 GET 部分(删 `_ensure_admin` / `_build_response_data` / PUT 三处),实现明文返回客户端 view新建 `common/logging/filters.py:AccessTokenMaskFilter` 作为 LOGGING.handlers 层防御性兜底
- **文件路径**:
- `aiapp/views.py`(修改 — 文件末尾追加 `_credential_slot_client_data_schema` 客户端响应 schema + `CredentialSlotClientView` APIView 类,仅 GET明文返回imports 段未动Phase 2 既有 `CredentialSlotAdminView` 未动)
- `qy_lty/urls.py`(修改 — imports 段追加 `from aiapp.views import CredentialSlotClientView``api_urlpatterns` 列表中追加 `path('credential-slot/', CredentialSlotClientView.as_view(), name='client_credential_slot')`,注册位置:`common/upload/` 之后、`v1/admin/` 之前)
- `common/logging/__init__.py`**新建** — 空文件,让 `common.logging` 成为可 import 的 Python 包)
- `common/logging/filters.py`**新建** — `AccessTokenMaskFilter(logging.Filter)` 类 + 4 个 regex 模式JSON / Python dict repr / URL query / 等号或冒号兜底)+ `filter()` 方法重写 `record.msg``record.args` 中的 access_token 字段值为 `mask_token(value)` 输出)
- `qy_lty/settings.py`(修改 — `LOGGING` 字典新增 `'filters'` 段(用 `'()': 'common.logging.filters.AccessTokenMaskFilter'` dictConfig 工厂语法);`'handlers'.aliyun``'handlers'.console` 各追加 `'filters': ['access_token_mask']`loggers 段 5 条 logger 完全未动)
- **修改类型**: 新增
- **修改内容**:
- 暴露 `GET /api/credential-slot/`(路径与管理端 `/api/v1/admin/credential-slot/` **完全分开**,客户端走 `/api/` 一级命名空间不进 `v1/admin/` 子路径):`RedisTokenAuthentication` + `IsAuthenticated`**不**做 is_staff 二次校验admin / user token 都允许admin 用户是手机用户超集CONTEXT 锁定决策);返回 `{ success, code, message, data: { app_id, access_token: <**明文**>, updated_at } }`Access Token 直接返回 `serializer.data`(不调 `mask_token`供手机端LTY_App_Project_URP/ 设备端LTY_Project实际调用阿里云 / 火山 / 腾讯第三方服务
- 新建 `AccessTokenMaskFilter`4 个正则模式覆盖 JSON 字符串(`"access_token":"VALUE"`、Python dict repr`'access_token':'VALUE'`、URL query`access_token=VALUE`)、等号或冒号兜底(`access_token: VALUE`)共 4 种序列化形态filter 同时改 `record.msg``record.args`(避免 Formatter 阶段再用 `%` 拼接出明文per RESEARCH Pitfall 2只匹配 `access_token` 字段名为前缀锚点,**不**误伤 `Authorization header:` / `Bearer` / 裸 user tokenper RESEARCH Pitfall 3filter 永远 `return True` 不丢弃 recordper RESEARCH Pitfall 1
- LOGGING dictConfig 注册filter 段用 `'()': '...'` 工厂语法(不是 `'class'`per RESEARCH Pitfall 5filter 挂在 `handlers.aliyun` / `handlers.console` 两个 handler 上(**不**挂 loggers 段per RESEARCH Pitfall 1 — 挂 logger 仅过滤直接通过该 logger 的 record挂 handler 才统一覆盖所有 logger → handler 路径);既有 5 条 logger 配置完全未动
- Swagger / ReDoc 自动暴露method-level `@swagger_auto_schema` 装饰器;响应 data schema 用独立 `_credential_slot_client_data_schema`access_token 字段 description 显式标注「明文 Access Token供手机/设备端实际调用第三方服务(管理端同接口会脱敏返回末 4 位)」,避免前端误解明文 / 脱敏
- 不引入新依赖(沿用 Django 4.2.13 + DRF + drf-yasg + Phase 1/2 落地的 `CredentialSlot.get_solo` / `CredentialSlotSerializer` / `mask_token`
- **修改原因**: Milestone v1.0「通用凭据槽位APP ID + Access Token」Phase 3 收尾 phase — 同时落地客户端读取CRED-05与日志脱敏CRED-06。客户端读取需要明文手机/设备端 Unity 调阿里云 / 火山 / 腾讯 SDK 时第三方 API 校验 token 字符级一致),所以 view 层不脱敏;但「明文走 view」会让任何后续开发者写 `logger.info(f"PUT body: {request.data}")` 类代码立即把 access_token 打到阿里云日志服务,所以新增 LOGGING.handlers 层 filter 作为防御性兜底。RESEARCH 已实证:当前仓库**没有**任何代码 logger 输出 `CredentialSlot.access_token` 明文(`StandardResponseMiddleware` 不打日志、view 不显式 logger 字段、Django 默认 access log 不含 body所以 CRED-06 的端到端验证靠**单元测试**伪造 LogRecord 验证 filter 行为4 种序列化形态 + 不误伤 Authorization 字段)+ 1 条端到端 logger.info 真实输出脱敏验证,不靠端到端找泄露路径。这是 CRED-06 的真实价值 — 防御性兜底,让未来代码改动天然安全
- **跨项目联动**: 无 — 客户端 GET `/api/credential-slot/` 给 Unity 客户端(`LTY_Project` / `LTY_App_Project_URP`)使用,那两个 repo 各自维护修改记录,不在本仓库范畴;`qy-lty-admin`Web 管理后台前端)**不消费**此接口(管理端走 Phase 2 落地的 `/api/v1/admin/credential-slot/`,由 admin token 鉴权 + 脱敏返回。CLAUDE.md 跨项目规则下:本 phase 既不影响 qy-lty-admin 也不与 Unity 客户端在同一仓库,故不在 qy-lty-admin/docs/修改记录.md 写互引条目Unity 客户端改动由 LTY_Project / LTY_App_Project_URP 在自身仓库各自记录
- **后续动作**: Milestone v1.0 至此完成;下一周期 milestone 候选见 `.planning/REQUIREMENTS.md` 「候选优先级」段HIGHACH-02 / SMS 频率限制 / DEBUG 收紧 / 测试基础设施 / 测试 MAC 硬编码MEDIUM好感度 P2-P4 / Python 版本升级 / device_interaction 拆分)
### [2026-05-07] Phase 2 — 管理端通用凭据槽位 REST 接口GET 脱敏 / PUT 覆写)
配套 Phase[.planning/phases/02-admin-rest/](.planning/phases/02-admin-rest/)