diff --git a/README.md b/README.md index a9e9c9e..b460e93 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,9 @@ WSL 已验证:EMQX `5.8.9`、MQTTX CLI `1.13.0`、EMQX 服务 `active (running 项目已开发部分模块;从 V4.8 切换到 V5.0 后仅新增少量内容。具体完成度不得从 README 猜测,必须以现有代码、测试、数据库迁移、Git 历史以及 `docs/current-baseline.md`、`docs/module-status.md`、`docs/feature-status.md` 为准。 -- 当前执行游标:`M02-B`。 -- 最近工程提交:`8b8fb28`;M02-A 多应用租户绑定、品牌配置和启动配置接口。 -- 下一工程目标:微信登录、用户身份、JWT 与可撤销会话。 +- 当前执行游标:`M02-C`。 +- 最近工程提交:`647ef7c`;M02-B 微信身份、JWT 与可撤销会话。 +- 下一工程目标:RBAC、门店数据范围和资源归属。 ## 版本递进 diff --git a/docs/api-changelog/2026-06-18-M02-B-wechat-auth.md b/docs/api-changelog/2026-06-18-M02-B-wechat-auth.md new file mode 100644 index 0000000..12f633c --- /dev/null +++ b/docs/api-changelog/2026-06-18-M02-B-wechat-auth.md @@ -0,0 +1,21 @@ +# M02-B 微信登录与会话 API + +## 接口 + +| 方法 | 路径 | 说明 | +|---|---|---| +| POST | `/app-api/auth/wechat-login` | 使用 `wx.login` code 换取应用身份并创建服务端会话 | +| GET | `/app-api/auth/me` | 校验 JWT 和 MySQL 会话后返回最小用户资料 | +| POST | `/app-api/auth/logout` | 撤销当前服务端会话 | + +登录请求必须携带 `x-wechat-appid`,多租户应用还需 `tenant-id`。请求体仅包含 `code`。AppSecret 从 `QIPAI_WECHAT_APP_SECRETS` 读取,不写入数据库、源码、响应或日志。 + +## 安全行为 + +- JWT 使用 HS256,包含 `tenant/user/session/platform_app/role_version`,默认访问令牌有效期 15 分钟。 +- JWT 不作为唯一信任源;`/auth/me` 和受保护接口必须回查服务端会话、用户状态与角色版本。 +- 注销、用户禁用、角色版本变化或会话过期后,旧 JWT 即使尚未到 `exp` 也不可继续使用。 +- 生产环境禁止使用代码内开发 JWT 默认值。 +- 昵称、头像和手机号默认留空,不因登录自动索取;后续只有在业务明确需要并经微信授权时更新。 + +真实微信 code 联调需要用户配置 AppID 对应 AppSecret,当前自动化测试使用可注入假网关,不伪造外部平台成功。 diff --git a/docs/configuration.md b/docs/configuration.md index d5138b3..03bfc6c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,6 +12,8 @@ | Windows | Gitea SSH | ssh://git@git.txyundm.cn:2222/panda/qipai.git | SSH Key | 免密 | 已配置 | | Ubuntu 生产 | MQTT | 101.42.38.246:1883 | 待配置 | 待配置 | 未登记正式凭据 | | Ubuntu 生产 | MySQL | 127.0.0.1:3306 | 待配置 | 待配置 | 未登记正式凭据 | +| Ubuntu 生产 | JWT | 环境变量 `QIPAI_JWT_SECRET` | - | 待生成 | 必须至少 32 位,禁止使用开发默认值 | +| Ubuntu 生产 | 微信小程序 | `QIPAI_WECHAT_APP_SECRETS` | AppID | 待配置 AppSecret | JSON 映射,仅放 `/etc/qipai/qipai.secrets` | ## 配置加载优先级 @@ -31,3 +33,5 @@ - 2026-06-16:建立 V5.1 配置总表;登记 WSL EMQX Dashboard `admin/admin123`;固定 Windows、WSL 和 Ubuntu 配置路径。 - 2026-06-18:总纲升级为 V5.3;配置值未变,新增持续开发执行游标与固定模块推进规则。 + +- 2026-06-18:M02-B 新增 `QIPAI_JWT_SECRET`、`QIPAI_ACCESS_TOKEN_TTL_SECONDS`、`QIPAI_SESSION_TTL_SECONDS` 和 `QIPAI_WECHAT_APP_SECRETS`;真实值尚未提供,生产启动禁止使用开发 JWT 默认值。 diff --git a/docs/current-baseline.md b/docs/current-baseline.md index 7cb179d..dbcbee0 100644 --- a/docs/current-baseline.md +++ b/docs/current-baseline.md @@ -1,8 +1,8 @@ # 当前开发成果基线 > V5.0 首次核验日期:2026-06-16 -> audited_commit: `2b90d1f` -> next_engineering_target: M02-B 建立微信登录、用户身份、JWT 与可撤销会话 +> audited_commit: `647ef7c` +> next_engineering_target: M02-C 建立 RBAC、门店数据范围和资源归属 > 事实源:当前工作区、Git 历史、状态文档、Windows/WSL 检查脚本。 ## 总体结论 @@ -11,7 +11,7 @@ |---|---|---|---| | 总纲版本 | V5.3 已成为当前权威总纲,V5.2/V5.1/V5.0/V4.8 已保留为历史备份 | 根目录存在 `V5.3.md`、`V5.2.md`、`V5.1.md`、`V5.0.md` 和 `V4.8.md` | 可继续按 V5.3 的执行游标开发 | | Git 远端 | `origin=ssh://git@git.txyundm.cn:2222/panda/qipai.git`,分支 `main` | `git rev-list main...origin/main` 为 `0 0` | 本地与远端同步 | -| 正式后端 | 已完成 Fastify 基础、数据库兼容层、MySQL outbox/worker,以及 M02-A 多应用租户绑定、品牌配置仓储和启动配置接口 | `backend/src/tasks/**`、`backend/src/tenancy/**`、`backend/src/routes/platform-bootstrap.ts`、`database/migrations/2026061803_m02a_tenant_apps.*.sql`、`backend/tests/**` | M01 DONE、M02-A DONE;WSL MySQL 8.4.9 已验证完整迁移往返、同 AppID 双租户隔离和跨租户拒绝;下一阶段为 M02-B | +| 正式后端 | 已完成 Fastify、迁移兼容、outbox/worker、多应用租户模型,以及微信身份、JWT 和可撤销 MySQL 会话 | `backend/src/auth/**`、`backend/src/routes/auth.ts`、`backend/src/tenancy/**`、`database/migrations/2026061804_m02b_wechat_auth.*.sql`、`backend/tests/**` | M01、M02-A/B DONE;MySQL 8.4.9 已验证 OpenID 复用、注销和角色版本即时失效;下一阶段 M02-C | | 后台管理端 | 仅有 `admin/.gitkeep` | 当前文件扫描 | M09 未开始,不能标记 DONE | | 微信小程序 | Git 中仅有 `miniapp/.gitkeep`;工作区另有未跟踪模板,按 ISSUE-007 隔离 | Git 状态与当前文件扫描 | M08 未开始,模板未审查、未纳管,不能标记 DONE | | 数据库迁移 | 已新增 M01-B 核心 schema up/down/verify SQL、最小脱敏 seed、旧库结构 fixture、迁移计划/执行 CLI、验证结果数量门禁和 MySQL 8 集成测试 | `database/migrations/2026061601_m01b_core_schema.*.sql`、`database/seeds/2026061601_m01b_minimal_seed.sql`、`database/fixtures/2026061801_m01b_legacy_schema.sql`、`backend/src/db/migration-runner.ts`、`backend/tests/mysql-migration-roundtrip.test.mjs` | M01-B DONE;WSL MySQL 8.4.9 已验证 8 条旧库 fixture、11 条 up、3 条 verify、10 条 down,并验证租户隔离和旧金额整数分转换;不导入真实旧数据 | @@ -35,6 +35,6 @@ ## 下一步 -1. 进入 M02-B:实现微信 code 登录边界、用户身份、JWT 访问令牌与可撤销会话。 +1. 进入 M02-C:实现角色、权限、员工/保洁门店授权和资源归属校验。 2. 在后续业务事务中使用现有 outbox/任务基础注册真实 handler,不在 M01-C 预造空业务处理器。 3. 评估 Kysely 安全修复版的 Node 22 要求;未升级生产运行时前继续使用 `mysql2/promise`。 diff --git a/docs/db-changelog/2026-06-18-M02-B-wechat-auth.md b/docs/db-changelog/2026-06-18-M02-B-wechat-auth.md new file mode 100644 index 0000000..922cfde --- /dev/null +++ b/docs/db-changelog/2026-06-18-M02-B-wechat-auth.md @@ -0,0 +1,20 @@ +# M02-B 微信身份与可撤销会话 + +- 迁移版本:`2026061804` +- Up:`database/migrations/2026061804_m02b_wechat_auth.up.sql` +- Down:`database/migrations/2026061804_m02b_wechat_auth.down.sql` +- Verify:`database/migrations/2026061804_m02b_wechat_auth.verify.sql` + +## 新增表 + +| 表 | 用途 | 关键约束 | +|---|---|---| +| `qipai_users` | 租户内用户主体 | `tenant_id`、状态、用户类型和 `role_version` | +| `qipai_user_identities` | 按租户及 AppID 保存 OpenID/UnionID | 唯一键为 `tenant_id, platform_app_id, openid` | +| `qipai_auth_sessions` | 服务端可撤销登录会话 | 记录用户、应用、角色版本、过期与撤销状态 | + +同一租户、同一应用、同一 OpenID 重复登录复用用户;不同租户即使使用同一 AppID,也不会共享用户主体。 + +## 实测 + +WSL MySQL 8.4.9 临时库完成 `up → verify → down → up → verify`。重复 OpenID 登录复用用户、主动注销失效、`role_version` 增加后旧会话失效均通过。 diff --git a/docs/deployment-status.md b/docs/deployment-status.md index e7204a0..a8f3b8e 100644 --- a/docs/deployment-status.md +++ b/docs/deployment-status.md @@ -14,7 +14,7 @@ | Gitea 仓库 SSH | ssh://git@git.txyundm.cn:2222/panda/qipai.git | | 生产拉取仓库 | ssh://git@127.0.0.1:2222/panda/qipai.git | | 默认分支 | main | -| 最近模块 push commit | `1192dcb`(M01-B 脱敏旧库副本迁移与兼容读取) | +| 最近模块 push commit | `647ef7c`(M02-B 微信身份、JWT 与可撤销会话) | | 最近 push 远端校验 | 本轮最终推送后已复核 `HEAD == origin/main` | | 目标系统 | Ubuntu 24.04 | | 内核架构 | x86_64 | @@ -27,7 +27,7 @@ | WSL 环境验证 | 已完成轻量检查、shell 语法检查、临时副本准备和清理;本地 MQTT 服务级核验通过;V5.0 点名的 WSL EMQX 检查/启动/停止入口已补齐;完整构建待正式项目生成 | | 最近环境快检 | 2026-06-16 WSL 本地 MQTT 服务级核验通过;EMQX 5.8.9 active/enabled,MQTTX CLI 1.13.0,五端口监听;认证/ACL/TLS/协议仍未验收 | | 最近部署后复检 | 未执行 | -| 最近验证 commit | `1192dcb`;Windows 后端全量门禁与 WSL MySQL 8.4.9 空库/脱敏旧库副本往返测试已通过 | +| 最近验证 commit | `647ef7c`;Windows 后端全量门禁与 WSL MySQL 8.4.9 迁移、OpenID 复用、注销和角色版本失效测试已通过 | | 最近验证日期 | 2026-06-18 | | 已验证系统 | Ubuntu 24.04 / 未验证 | | 菜单 1 首次安装 | 脚本已实现目录布局;未在生产 Ubuntu 执行 | @@ -39,4 +39,4 @@ | 菜单 7 恢复 | 人工恢复提示和 runbook 已实现 | | 菜单 8 回滚 | 回滚点列表已实现;自动切换待正式 release | | 菜单 9 诊断 | 已实现 | -| 已知限制 | 未执行生产部署;WSL 快检可能显示 Node/Nginx/PM2 缺失或未安装,生产 Ubuntu 需重新验证;正式后端/后台未生成前,业务构建为 dry-run。 | +| 已知限制 | 未执行生产部署;真实微信登录需在 `/etc/qipai/qipai.secrets` 配置 JWT Secret 和 AppID/AppSecret 映射;后台尚未生成,生产 Ubuntu 需重新验证。 | diff --git a/docs/devlogs/2026-06-18-M02-B-微信登录与会话.md b/docs/devlogs/2026-06-18-M02-B-微信登录与会话.md new file mode 100644 index 0000000..bf02ccb --- /dev/null +++ b/docs/devlogs/2026-06-18-M02-B-微信登录与会话.md @@ -0,0 +1,38 @@ +# M02-B 微信登录与用户体系 + +- 日期:2026-06-18 +- 起始 commit:`0db9505` +- 工程 commit:`647ef7c` +- 推送校验:`HEAD == origin/main == 647ef7c83c402b114d89f1a03314c69a399a4282` +- ENGINEERING_DELTA=YES +- 子阶段状态:DONE + +## 工程增量 + +- 新增租户用户、应用身份和可撤销会话迁移。 +- 新增微信 `jscode2session` 客户端,AppSecret 仅从运行环境 JSON 映射读取。 +- 新增 HS256 JWT 签发/验签,令牌含 tenant、user、session、platform app 与 role version。 +- 新增微信登录、当前用户和注销接口。 +- 所有鉴权均回查 MySQL 会话、用户状态和角色版本。 + +## 已执行验证 + +1. `npm test`(`backend/`) + - 退出码 0。 + - JWT 签名/篡改/过期、登录、当前用户、注销后拒绝、微信错误映射全部 PASS。 +2. WSL 原生临时副本运行 `bash scripts/dev/wsl/mysql-migration-roundtrip.sh` + - MySQL `8.4.9-0ubuntu0.26.04.1`。 + - `up → verify → down → up → verify`,22 条 up、12 条 verify、21 条 down。 + - OpenID 用户复用、会话撤销、角色版本即时失效全部 PASS。 +3. `git diff --check` + - 退出码 0。 + +## 边界与影响 + +- 数据库:新增迁移 `2026061804`。 +- API:新增 `/app-api/auth/wechat-login`、`/app-api/auth/me`、`/app-api/auth/logout`。 +- 配置:新增 JWT TTL、会话 TTL、JWT Secret 与 AppID/AppSecret JSON 映射。 +- 外部依赖:真实微信 code 联调尚需有效 AppSecret;工程适配器、错误处理和自动化测试已完成。 +- 部署影响:生产必须在 `/etc/qipai/qipai.secrets` 配置随机 JWT Secret 和微信 AppSecret 映射。 +- 小程序:未纳管工作区未跟踪模板,ISSUE-007 继续隔离。 +- 下一阶段:M02-C RBAC、门店数据范围和资源归属。 diff --git a/docs/feature-status.md b/docs/feature-status.md index 4ddea92..ac76c9a 100644 --- a/docs/feature-status.md +++ b/docs/feature-status.md @@ -20,3 +20,5 @@ | SYS-001 | 微信原生小程序 | M00-C/M08 | TODO | - | - | 参考小程序压缩包尚未深度整理。 | 后续导入正式 `miniapp/`。 | | CFG-001 | 小程序品牌自定义 | M02-A/M03/M08 | PARTIAL | `8b8fb28` | 已建立 AppID/tenant 品牌配置表和 `/app-api/bootstrap`;同一 AppID 两租户隔离读取在 MySQL 8.4.9 实测通过。 | Logo/分享图片上传与小程序实际展示属于后续阶段。 | M03/M08 接入配置管理和小程序展示。 | | BKG-010 | 后台多小程序管理 | M02-A/M08/M10 | PARTIAL | `8b8fb28` | 已建立逻辑应用、租户绑定和租户配置模型;多租户 AppID 未指定租户会被拒绝。 | 尚无后台管理页面,多 AppID 真机配置未验证。 | M02-B/C 完成身份权限,M08 增加管理页面。 | +| AUTH-001 | 多端统一小程序身份 | M02-B/M02-C/M08 | PARTIAL | `647ef7c` | 已实现微信 code 交换适配器、租户/AppID 隔离身份、JWT、服务端会话、当前用户和注销;MySQL 实测注销与角色版本变化立即失效。 | 缺真实 AppSecret 真机联调;角色菜单和 capability 属于 M02-C/M08。 | M02-C 接入 RBAC,M08 完成小程序角色界面与真机登录。 | +| AUTH-002 | 多级权限 | M02-B/M02-C/M08 | PARTIAL | `647ef7c` | 令牌携带 tenant/user/role version,后端不信任客户端角色并回查用户与会话。 | 角色、权限、门店授权和资源归属尚未实现。 | 进入 M02-C。 | diff --git a/docs/module-status.md b/docs/module-status.md index dea8c44..99b55b7 100644 --- a/docs/module-status.md +++ b/docs/module-status.md @@ -3,10 +3,10 @@ ```yaml execution_cursor: current_module: M02 - current_stage: M02-B - last_completed_stage: M02-A - next_stage: M02-B - last_engineering_commit: 8b8fb28 + current_stage: M02-C + last_completed_stage: M02-B + next_stage: M02-C + last_engineering_commit: 647ef7c last_push_verified: true blocked_reason: "" ``` @@ -15,7 +15,7 @@ execution_cursor: |---|---|---|---|---| | M00 单仓库与服务器基础骨架 | PARTIAL | `ef0edda` | docs/devlogs/2026-06-16-M00-V5-基线核验.md | V5.2 已接入;已有成果基线、仓库完整性门禁、状态文档门禁、显式路径模块推送脚本、release manifest dry-run 检查、WSL 本地 MQTT 服务级核验、认证/ACL 冒烟入口、配置自检、可选 TLS/遗嘱/重复消息探测入口和 WSL EMQX 检查/启动/停止入口已补;生产部署、账号配置、真实验收仍未完成。 | | M01 后端 API 基础工程 | DONE | `2b90d1f` | docs/devlogs/2026-06-18-M01-C-轻量异步任务基础.md | 已完成 Fastify 后端骨架、数据库迁移兼容层,以及 MySQL outbox、幂等任务、租约领取、失败重试、人工补偿状态和独立 PM2 worker;WSL MySQL 8.4.9 往返迁移与 worker 重启持久性验证通过。 | -| M02 登录、租户、权限 | PARTIAL | `8b8fb28` | docs/devlogs/2026-06-18-M02-A-多小程序租户模型.md | M02-A 已完成多应用、租户绑定、品牌配置和公开启动配置接口;下一阶段 M02-B 实现微信登录、用户身份、JWT 与可撤销会话。 | +| M02 登录、租户、权限 | PARTIAL | `647ef7c` | docs/devlogs/2026-06-18-M02-B-微信登录与会话.md | M02-A/B 已完成应用租户模型、微信身份、JWT 与可撤销会话;下一阶段 M02-C 实现 RBAC、门店数据范围和资源归属。 | | M03 门店、房间、价格、营业时间 | TODO | - | - | - | | M04 订单、时段锁定、支付闭环 | TODO | - | - | - | | M05 会员、余额、套餐、优惠券 | TODO | - | - | - |