系统架构设计
做密钥权限审计这种项目,第一件事不是写代码,而是把架构想清楚。我记得早年有个同事上来就撸接口,结果做到一半发现模块之间耦合得像一团麻线……重构了两次,工期全搭进去了。所以这一章,我们先搭好骨架。
整体架构图
先给你看一张我画的核心架构图。它把认证、密钥管理、权限、审计四个模块串起来,中间用 Redis 做缓存,背后挂 MySQL。你仔细看这张图,后面所有的代码分层、接口设计、数据流都从这里来。
架构核心思路: 四个模块各自独立,通过明确的接口契约交互。业务核心层不直接操作数据库,全部经过数据层统一封装。这样做的好处是——你改了一个模块的存储方式,其他模块基本不用动。我曾经在旧项目里因为审计日志和业务逻辑写在一起,导致替换存储引擎时改了几十个文件……痛过之后才懂了。
模块划分
我来拆解一下每个模块到底管什么事、管多宽。
1. 认证模块
说白了就是「你是谁」。这个模块只做一件事:校验调用方身份。我习惯用 JWT + 动态密钥的方式,每次签发令牌时把密钥版本写入 payload,Redis 里存当前有效版本。这样密钥轮换时,旧令牌直接失效,不用等自然过期。
- 职责: 签发、校验、刷新、吊销
- 对外暴露:
/auth/login、/auth/refresh、/auth/revoke - 缓存策略: 黑名单放 Redis,设置 TTL 等于原 剩余时间
2. 密钥管理模块
这个模块管理所有被审计的密钥资源。创建密钥、更新密钥、删除密钥、查询密钥元信息。注意——密钥本身的密文不落地,只存哈希指纹。为什么?
- 职责:密钥全生命周期管理(CRUD + 版本控制)
- 对外暴露:
/keys资源接口 - 避坑:我曾经让密钥明文经过日志打印,差点出事……后来强制在 ORM 层对敏感字段做序列化脱敏
3. 权限校验模块
权限模块决定了「你能干什么」。我们这里采用 RBAC 模型(基于角色的访问控制)。用户 -> 角色 -> 权限,一级级映射。每次请求过来,权限模块从 Redis 里拿用户角色和资源权限集做匹配,命中则放行,不命中则返回 403。
- 职责:权限规则解析、资源鉴权、动态权限刷新
- 缓存设计:角色-权限映射存 Redis Hash,用户-角色映射存 String
4. 审计日志模块
审计日志是事后追溯的唯一凭证。每个接口调用成功或失败,都要记录:谁(用户 ID)、什么时候(时间戳)、做了什么(操作类型 + 资源 ID)、结果(成功/失败/拒绝)。
我建议异步写入——用 Python 的 ThreadPoolExecutor 或者消息队列,把日志写操作丢到后台线程,主线程只管校验和返回。
- 职责:操作日志记录、查询、归档
- 存储策略:当日日志存 MySQL 主表,超过 30 天的自动转移到历史表或归档到对象存储
| 模块 | 核心表(MySQL) | Redis 用途 |
|---|---|---|
| 认证 | users |
黑名单、当前密钥版本 |
| 密钥管理 | keys, key_versions |
密钥摘要缓存(防重复读库) |
| 权限校验 | roles, permissions, user_roles |
角色-权限 Hash,用户角色缓存 |
| 审计日志 | audit_logs |
日志写入队列(临时缓冲) |
技术栈选型
选型这件事,没有银弹。我们这套课程用 FastAPI 做主框架,因为异步特性对 IO 密集场景更友好。MySQL 8.0 做持久层,Redis 6.x 做缓存和限流。我给你列个清单:
- FastAPI:Python 3.10+,异步路由,自动 OpenAPI 文档
- SQLAlchemy 2.0 (异步模式):ORM 层,用 Alembic 做迁移
- Redis (aioredis):缓存 + 限流器(滑动窗口算法)
- PyJWT:JWT 签发与校验
- Pydantic:请求与响应模型校验
- pytest + pytest-asyncio:测试框架
我的小建议: 如果你团队里有人习惯 Flask,也可以换,但异步支持需要靠 gevent 或者手动配线程池,不如 FastAPI 省心。我们这章之后的所有代码,我都用 FastAPI 写,你跟着跑就行了。
模块间的协作流程
光有图不够,得跑通一个完整流程。比如「用户 A 调用 GET /keys/123」——系统怎么走?
- 请求到达 API 网关,认证过滤器提取 JWT,校验签名和有效期,通过后把用户信息塞进请求上下文。
- 网关将请求转发给密钥管理模块的
/keys/123路由。 - 密钥管理模块先调用权限校验模块,查询用户 A 对资源
keys:123是否有read权限。 - 权限模块查 Redis 缓存:用户 A 的角色 -> 角色含有的权限集。匹配成功返回 True。
- 密钥管理模块从 MySQL 查出密钥元信息(不包含明文),拼接响应返回。
- 审计日志模块异步记录一条日志:{"user_id":"A","action":"read","resource":"keys/123","result":"success","timestamp":"..."}
注意! 权限校验如果失败,审计日志也要记录,只不过 result 字段写 deny。这样你以后排查「谁在尝试越权操作」时就有依据了。我有个客户上线后忘了记录拒绝日志,结果被黑了三天才追到异常 IP……血的教训。