Files
doodle-store/docs/v3.5/服务端登录接入说明.md
T

101 lines
8.7 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.
# 小程序静默登录与服务端鉴权接入
> 日期:2026-10-15。范围:doodle-store 服务端;小程序 HTTP 适配、资料修改/注销、七牛头像签名和 Web 登录另行实现。
设计依据:[用户鉴权与登录设计](./用户鉴权与登录设计.md)、[数据库设计](./数据库设计文档.md)。
## 1. 本次接口
统一前缀为 `/api/v1`,开发文档为 `/api-docs`。
| 接口 | 身份要求 | 行为 |
| ------------------------- | --------------------------------- | -------------------------------------------------- |
| `POST /auth/wechat/login` | 无本站 token,必须有真实微信 code | 验证微信身份、创建或恢复账户,签发默认 24 小时 JWT |
| `GET /me` | 本站 Bearer JWT | 查询当前账户的本人摘要和当前 role |
| `GET /health` | 公开 | HTTP 进程存活,不检查外部服务 |
| `GET /health/ready` | 公开 | 检查 PostgreSQL 连通性,不代表微信或七牛可用 |
小程序通过 `wx.login` 获得 code 后提交:
```http
POST /api/v1/auth/wechat/login
Content-Type: application/json
{
"code": "本次 wx.login 返回的 code",
"mode": "start"
}
```
- `start` 允许首次建号,已有用户仍沿用原 ID;默认 role=user、status=active、authVersion=1。
- `resume` 只恢复已有用户,账户不存在时返回 401/ACCOUNT_NOT_FOUND,不自动重建。
- 两个模式都先向微信验证 code,且都拒绝 disabled 用户;不能使用 start 重置禁用状态、角色或资料。
- 请求不接受 AppID、AppSecret、OpenID、UnionID、userId 或角色等额外字段;应用凭据固定取服务端配置。
成功结果为 `{code:0,message:"ok",data:{accessToken,tokenType,expiresIn,expiresAt,user}}`。user 只含 `id/nickName/avatarUrl/avatarUrlExpiresAt/createdAt`;`GET /me` 另含当前 role,不输出微信身份或凭证版本。
`expiresIn/expiresAt` 从实际 JWT claims 取得;后续请求使用 `Authorization: Bearer <accessToken>`。登录、本人接口的成功与失败响应均为 `Cache-Control: no-store`。
当前七牛签名未接入,头像 URL 和期限返回 NULL;数据库已有头像引用保留,不改成公开对象地址。资料修改、清资料、注销及账户版本主动递增服务不包含在本次接口中。
## 2. 鉴权与恢复
受保护接口默认执行:固定算法/issuer/audience 的 JWT 验证 → 查询当前 users → 核对 active 和 authVersion → 检查服务端 role。账户不存在、被禁用或版本失效时旧 token 立即被拒绝;角色变化无需等待 token 到期。
`GET /me` 的 userId 只取守卫上下文,不允许指定其他账户;即使 query 中出现 userId,也不会用于身份选择。管理 API 仍需 `@Roles('admin')` 和对应业务归属/写事务校验;本次没有新增公开管理员登录或任意用户 token 签发接口。
错误保留 HTTP 状态与 `{code,message,requestId}`,仅可信业务异常增加有限 reason:
| HTTP | reason | 小程序下一步 |
| ---- | ------------------------------------------- | ------------------------------------------------- |
| 401 | AUTH_REQUIRED / AUTH_INVALID / AUTH_EXPIRED | 按设计共用一次静默恢复;原业务最多重发一次 |
| 401 | AUTH_REVOKED | 停止后台恢复,用户主动继续时再验证微信 |
| 401 | ACCOUNT_NOT_FOUND | 清旧状态,停止后台建号;主动开始时才使用 start |
| 401 | ACCOUNT_DISABLED | 停止恢复;重新登录不解除禁用 |
| 400 | WECHAT_CODE_INVALID | 当前 code 不能重用;受总重试次数限制地获取新 code |
| 403 | WECHAT_LOGIN_BLOCKED | 微信风控拦截,停止自动重试 |
| 429 | RATE_LIMITED | 遵守 Retry-After,不能立即循环 |
| 503 | AUTH_UPSTREAM_UNAVAILABLE | 微信网络/配置/响应异常,退避;不传播微信 errmsg |
| 503 | DATABASE_UNAVAILABLE | 数据库暂不可用,不降级为只验 JWT 放行 |
微信调用固定 HTTPS 地址,5 秒超时,禁止重定向,不自动重试一次性 code。每分钟单 IP 最多 30 次登录兑换、可信 OpenID 最多 10 次;限流使用有界内存,进程重启会重置,当前用于单实例。来源 IP 按已配置的可信代理解析,不接受任意客户端自报的 X-Forwarded-For。
不记录 code、OpenID/UnionID、AppSecret、session_key、token 或原始微信错误。session_key 没有当前业务用途,因此不落库、不缓存、不下发。
## 3. 配置与启动
服务端只加载 `apps/server/.env`,以下配置现在都必须有效存在:
- `DATABASE_URL`:本地 PostgreSQL 连接串,使用 public schema。
- `WECHAT_MINI_APP_ID` 与 `WECHAT_MINI_APP_SECRET`:对应当前小程序,不能与 JWT_SECRET 混用。
- `JWT_SECRET`:至少 32 字节的独立密钥;issuer/audience/期限继续沿用 Server 基础设置。
从 doodle-store 根目录运行 `pnpm dev`。Prisma 客户端在 dev/build/typecheck/test 前生成;实际查询按需建立连接,HTTP 健康检查不因数据库故障被误判为进程退出。应用关闭时释放连接池。
真实 code 必须由相同 AppID 的小程序现取,不能用文档占位值或在公网开放测试后门。微信成功登录还需要合法请求域名、HTTPS 与真机/体验版联调;自动测试替换微信 HTTP 边界,不代表已经验证真实微信账号登录。
## 4. 数据库与迁移
Prisma Schema 映射现有 13 张表,客户端仅服务端可用。`0_v35_baseline` 保留原始 SQL 的 CHECK、C 排序规则、部分唯一索引和初始配置;不能用 Prisma 自动生成的简化 DDL 覆盖这些约束。
已有数据库在完整核对后,使用 `pnpm --filter @doodle/server exec prisma migrate resolve --applied 0_v35_baseline` 登记基线;该操作新增 Prisma 迁移记录,不重建业务表。空库使用 `pnpm --filter @doodle/server exec prisma migrate deploy`,会执行基线及初始配置。
后续升级新增迁移文件,已登记的基线保持不变。`sql/004`~`008` 是旧库升级档案,不在最新基线之上重跑;日常开发不使用 db push 或 reset 改写已建好的库。
登录先在事务外验证微信,再以 OpenID 唯一约束和用户行锁处理账户。并发 start 使用 INSERT ON CONFLICT DO NOTHING,不覆盖既有状态;resume 缺用户不写入。普通登录仅维护活跃时间;UnionID 只补空,不清空、不自动合并;非空冲突记录不含身份值的警告。
## 5. 验证方式
- `pnpm check`:格式、ESLint、类型、构建及无需真实数据库的测试;数据库专项测试在未提供隔离连接时跳过。
- `pnpm test:db`:从本地开发连接取得建库权限,创建随机命名的独立短期库,执行正式迁移,运行 HTTP + 真实 Prisma/PostgreSQL 测试,结束后删除短期库。
数据库专项脚本仅允许本机 doodle_dev 实例,测试代码还验证随机短期库名称;不会清空开发库,也不在开发库写入假用户。覆盖老 ID 延续、并发首次登录、大小写语义、resume 不建号、UnionID 保留、禁用/降权/撤销/删除、本人读取、参数注入、上游错误、限流及日志脱敏。
微信接口依据:[code2Session](https://developers.weixin.qq.com/miniprogram/dev/server/API/user-login/api_code2session.html)。Prisma 接入参考:[CommonJS 客户端生成](https://www.prisma.io/docs/orm/v7/prisma-schema/overview/generators)、[既有数据库基线](https://www.prisma.io/docs/orm/v7/prisma-migrate/workflows/baselining)。
## 6. 本次验证记录
2026-10-15:`pnpm check` 通过,20 项常规测试通过;数据库专项在常规运行时按设计跳过,已通过独立 `pnpm test:db` 执行,8 项检查全部通过。真实微信调用使用 HTTP 替身验证固定地址、凭据来源、错误映射和一次性 code 不重试,尚未进行小程序真实 code 联调。
构建产物使用本机真实配置启动,HTTP 存活和数据库就绪均 200,本人接口无凭证 401,空 code 登录请求 400;检查服务已关闭。本地基线已登记,Prisma migrate diff 无差异;13 张业务表及两条初始配置保留,开发库没有写入测试用户,所有临时测试/参考库已删除。真实密钥未进入可提交文件。