Files
datahub/docs/api_key_validate_flow.md
T
2026-04-17 11:06:34 +08:00

99 lines
3.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API Key 认证流程
## 概述
API Key 认证是系统支持的两种认证方式之一(另一种是 JWT)。客户端通过 `X-API-Key` 请求头传递明文 key,中间件负责解析、校验并将认证用户注入到 request attribute 中,供下游中间件和控制器使用。
## API Key 格式
明文格式:`{user_id}#{token}`
- `user_id`:用户 ID(整数)
- `#`:分隔符
- `token`64 字符十六进制随机串(`bin2hex(random_bytes(32))`
示例:`42#a3b5c7d9e1f2...`(完整 token 为 64 字符)
数据库仅存储 token 的 SHA-256 哈希值(`key_hash`),不存储明文。明文仅在生成时返回给用户一次。
## 认证流程图
```mermaid
flowchart TD
A["请求进入 AuthMiddleware"] --> B{"有 Bearer Token?"}
B -- 是 --> JWT["JWT 认证路径"]
B -- 否 --> C{"有 X-API-Key 头?"}
C -- 否 --> R401_NO["401 未授权,请先登录"]
C -- 是 --> D["解析明文 key"]
D --> E{"包含 # 分隔符?"}
E -- 否 --> R401_FMT["401 API Key 无效或已过期"]
E -- 是 --> F["拆分 user_id 和 token sha256(token) → key_hash"]
F --> G["查询 api_keys 表WHERE (user_id, key_hash)AND 未过期"]
G --> H{"记录存在?"}
H -- 否 --> R401_KEY["401 API Key 无效或已过期"]
H -- 是 --> I{"key.enabled?"}
I -- false --> R403_KEY["403 该 API Key 已被禁用"]
I -- true --> J["加载关联 user"]
J --> K{"user 存在且user.status = 1?"}
K -- 否 --> R403_USER["403 账号已被禁用"]
K -- 是 --> L{"user.api_key_enabled?"}
L -- false --> R403_FEAT["403 API Key 功能未启用"]
L -- 是 --> M["认证成功"]
M --> N["更新 last_used_at"]
N --> O["注入 request attribute\nauth_user → User\nauth_type → 'api_key'"]
O --> P["写入协程 Context"]
P --> Q["放行到下游 handler"]
style R401_NO fill:#fdd,stroke:#c00
style R401_FMT fill:#fdd,stroke:#c00
style R401_KEY fill:#fdd,stroke:#c00
style R403_KEY fill:#fdd,stroke:#c00
style R403_USER fill:#fdd,stroke:#c00
style R403_FEAT fill:#fdd,stroke:#c00
style M fill:#dfd,stroke:#0a0
style JWT fill:#def,stroke:#08c
```
### 关键 SQL
解析阶段的数据库查询走 `(user_id, key_hash)` 联合索引,单次精确命中:
```sql
SELECT * FROM api_keys
WHERE user_id = {user_id}
AND key_hash = {hash}
AND (expires_at IS NULL OR expires_at > NOW())
LIMIT 1
```
## 数据查询汇总
整个认证流程涉及 2 次数据库查询 + 1 次写入:
| 操作 | SQL | 说明 |
|------|-----|------|
| 查找 API Key | `SELECT FROM api_keys WHERE (user_id, key_hash)` | 联合索引精确匹配 |
| 加载关联用户 | `SELECT FROM users WHERE id = {user_id}` | `$api_key->user` 懒加载 |
| 更新使用时间 | `UPDATE api_keys SET last_used_at = NOW()` | 记录最后使用时间 |
## 下游消费
认证完成后,下游代码通过以下方式获取认证用户:
- **中间件**`$request->getAttribute('auth_user')` 或从 Context 读取
- **控制器**`$this->getAuthUser()`AbstractController 提供,含 JWT guard 兜底)
## 相关文件
| 文件 | 职责 |
|------|------|
| `app/Middleware/AuthMiddleware.php` | 认证入口,执行校验链 |
| `app/Model/ApiKey.php` | key 解析、DB 查询、格式定义 |
| `app/Controller/AbstractController.php` | `getAuthUser()` 统一用户获取 |