feat: add WeChat session authentication

This commit is contained in:
2026-07-20 14:47:16 +08:00
parent 2ada52f35a
commit 67d29d3ca0
26 changed files with 1289 additions and 130 deletions

View File

@@ -8,7 +8,7 @@
- OpenAPI JSON`GET /openapi.json`
- 完整调用流程:[API_GUIDE.md](./API_GUIDE.md)
Scalar 中的业务接口已预填开发用户 ID 和请求体示例。创建、列表、预设和健康检查可以直接发起请求;按 ID 操作时,应使用创建或列表接口返回的真实 ID。
Scalar 中的业务接口需要填写微信登录换取的 Bearer 令牌,请求体接口提供了示例。按 ID 操作时,应使用创建或列表接口返回的真实 ID。
## 启动和数据库策略
@@ -22,6 +22,7 @@ Scalar 中的业务接口已预填开发用户 ID 和请求体示例。创建、
cd server
cp .env.example .env
# 在 .env 中填写 DATABASE_URL
# 同时填写 WECHAT_APP_ID 和 WECHAT_APP_SECRET
cargo run --bin migrate
cargo run
```
@@ -39,7 +40,7 @@ ASR_WORKER_CONCURRENCY=1
ASR_JOB_LEASE_SECONDS=3600
```
在项目根目录执行 `docker compose -f compose.asr.yml up --build -d` 启动中文模型。`GET /health``speech_available=true` 表示模型已下载并可以接收任务。
在项目根目录执行 `docker compose -f compose.asr.yml up --build -d` 启动中文模型。`GET /health``speech_available=true` 表示模型已下载并可以接收任务`wechat_auth_configured=true``development_auth_enabled=false` 表示生产微信鉴权已就绪
腾讯云仅作为可选提供方保留。需要切换时配置:
@@ -59,21 +60,30 @@ FEEDBACK_SUMMARY_API_KEY=your-api-key
FEEDBACK_SUMMARY_MODEL=your-model
```
## 临时身份边界
## 微信登录和会话
目前所有业务接口都要求 `X-User-Id` 请求头,值为 UUID。例如
小程序调用 `wx.login()`,再把一次性 code 发送到
```text
X-User-Id: 5a4da7f0-d70c-465f-bcab-124c504aa9f0
POST /api/v1/auth/wechat/login
```
这是微信登录接入完成前的开发身份边界,用于确保每位教师只能访问自己的记录。接入微信登录后,后端会从登录凭据解析用户身份,客户端不再直接提供此请求头。
Rust API 使用服务端 `WECHAT_APP_ID``WECHAT_APP_SECRET` 调用微信 `code2Session`,把 `openid` 映射为内部教师 UUID并返回 30 天有效的随机会话令牌。业务接口统一使用:
```text
Authorization: Bearer <access-token>
```
数据库仅保存令牌的 SHA-256 哈希AppSecret 和微信 session_key 不会返回客户端。本地兼容头只有设置 `ALLOW_DEVELOPMENT_USER_HEADER=true` 时才启用,生产 Compose 强制为 `false`
## 接口
| 方法 | 路径 | 说明 |
| --- | --- | --- |
| `GET` | `/health` | 服务、数据库、语音提供方及模型可用状态。 |
| `POST` | `/api/v1/auth/wechat/login` | 用 `wx.login` code 换取后端会话。 |
| `GET` | `/api/v1/auth/me` | 读取当前教师身份和会话状态。 |
| `POST` | `/api/v1/auth/logout` | 撤销当前会话。 |
| `GET` | `/api/v1/profiles` | 列出学生档案;可选 `query``grade``subject` 参数。 |
| `POST` | `/api/v1/profiles` | 创建学生档案。 |
| `GET` | `/api/v1/profiles/{profile_id}` | 读取单个学生档案。 |