feat: 实现 C 端完整认证系统(client-auth-system)
实现面向个人客户的 7 个认证接口(A1-A7),覆盖资产验证、 微信公众号/小程序登录、手机号绑定/换绑、退出登录完整流程。 主要变更: - 新增 PersonalCustomerOpenID 模型,支持多 AppID 多 OpenID 管理 - 实现有状态 JWT(JWT + Redis 双重校验),支持服务端主动失效 - 扩展微信 SDK:小程序 Code2Session + 3 个 DB 动态工厂函数 - 实现 A1 资产验证 IP 限流(30/min)和 A4 三层验证码限流 - 新增 7 个错误码(1180-1186)和 6 个 Redis Key 函数 - 注册 /api/c/v1/auth/* 下 7 个端点并更新 OpenAPI 文档 - 数据库迁移 000083:新建 tb_personal_customer_openid 表
This commit is contained in:
135
openspec/changes/client-auth-system/proposal.md
Normal file
135
openspec/changes/client-auth-system/proposal.md
Normal file
@@ -0,0 +1,135 @@
|
||||
## Why
|
||||
|
||||
系统需要一套面向个人客户(C 端)的完整认证体系,替代已删除的旧 H5 登录接口。客户端(微信公众号 H5 / 微信小程序)的登录流程与 B 端完全不同:基于**资产标识符**而非用户账号密码,先验证资产 → 再微信授权 → 自动绑定资产 → 可选绑定手机号。同时,公众号和小程序可能使用不同 AppID 且不一定绑定同一微信开放平台,需要支持多 OpenID 管理。
|
||||
|
||||
**前置依赖**:提案 0(`client-api-data-model-fixes`)已完成 PersonalCustomer.wx_open_id 索引变更和旧接口删除。
|
||||
|
||||
## What Changes
|
||||
|
||||
### 新增模型
|
||||
|
||||
- **PersonalCustomerOpenID**:个人客户 OpenID 关联表,支持同一客户在不同 AppID(公众号/小程序)下的多 OpenID 记录。唯一索引 `UNIQUE(app_id, open_id) WHERE deleted_at IS NULL`
|
||||
|
||||
### 认证接口(`/api/c/v1/auth/`)
|
||||
|
||||
- **A1 验证资产标识符** `POST /verify-asset`:无需认证。输入 SN/IMEI/虚拟号/ICCID/MSISDN → 返回 `asset_token`(短时效 JWT,5 分钟过期,payload 含 asset_type + asset_id)。IP 级别限频(30 次/分钟)防暴力枚举。不暴露内部 asset_id
|
||||
- **A2 微信公众号登录** `POST /wechat-login`:无需认证。用微信 OAuth code + asset_token → 查找/创建客户 → 绑定资产 → 签发有状态 JWT Token(Redis 存储)→ 返回 token + 是否需要绑定手机号
|
||||
- **A3 微信小程序登录** `POST /miniapp-login`:无需认证。用小程序 jscode2session + asset_token → 同 A2 后续流程
|
||||
- **A4 发送验证码** `POST /send-code`:无需认证。限频:同手机号 60s、同 IP 20 次/小时、每手机号 10 次/天
|
||||
- **A5 绑定手机号** `POST /bind-phone`:需 JWT。首次绑定,检查重复
|
||||
- **A6 换绑手机号** `POST /change-phone`:需 JWT。双重验证码(旧+新手机)
|
||||
- **A7 退出登录** `POST /logout`:需 JWT。删除 Redis token 记录
|
||||
|
||||
### 基础设施
|
||||
|
||||
- **有状态 JWT Token 管理**:JWT payload 仅含 `customer_id` + `exp`,Redis 存储 token 有效状态,支持服务端主动失效(封禁/强制下线)
|
||||
- **PersonalAuthMiddleware 增强**:增加 Redis 有效性检查,token 不在 Redis 中则拒绝
|
||||
- **统一资产解析公共方法** `resolveAssetFromIdentifier()`:个人客户调用不走 shop_id 数据权限过滤
|
||||
- **OpenID 安全规范**:所有需要 OpenID 的接口(支付、充值),OpenID 由后端根据 `customer_id` + `app_type` 查 PersonalCustomerOpenID 表获取,禁止客户端传入
|
||||
- **手机号绑定配置**:通过 Viper 配置 `client.require_phone_binding`(boolean),登录时检查并返回 `need_bind_phone` 标识
|
||||
|
||||
### 登录完整流程
|
||||
|
||||
```
|
||||
用户打开客户端
|
||||
│
|
||||
▼
|
||||
输入资产标识符(SN/IMEI/虚拟号/ICCID)
|
||||
│
|
||||
▼
|
||||
[A1] POST /verify-asset ──→ 返回 asset_token(5分钟有效)
|
||||
│
|
||||
▼
|
||||
微信授权(前端完成)
|
||||
│
|
||||
├─── 公众号 ──→ [A2] POST /wechat-login (code + asset_token)
|
||||
│
|
||||
└─── 小程序 ──→ [A3] POST /miniapp-login (code + asset_token)
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ 解析 asset_token │
|
||||
│ 获取微信 openid │
|
||||
│ 查找/创建客户 │
|
||||
│ 绑定资产 │
|
||||
│ 签发 JWT + Redis │
|
||||
└──────┬───────────┘
|
||||
│
|
||||
▼
|
||||
返回 { token, need_bind_phone, is_new_user }
|
||||
│
|
||||
▼
|
||||
need_bind_phone == true?
|
||||
│ │
|
||||
YES NO
|
||||
│ │
|
||||
▼ ▼
|
||||
[A4] 发送验证码 进入主页面
|
||||
[A5] 绑定手机号
|
||||
│
|
||||
▼
|
||||
进入主页面
|
||||
```
|
||||
|
||||
### 客户查找/创建逻辑(A2/A3 共享)
|
||||
|
||||
```
|
||||
收到 openid + (可选)unionid
|
||||
│
|
||||
▼
|
||||
查 PersonalCustomerOpenID WHERE app_id=当前AppID AND open_id=openid
|
||||
│
|
||||
├── 找到 → 获取 customer_id → 已有客户
|
||||
│
|
||||
└── 没找到
|
||||
│
|
||||
▼
|
||||
有 unionid?
|
||||
│
|
||||
├── YES → 查 PersonalCustomerOpenID WHERE union_id=unionid
|
||||
│ │
|
||||
│ ├── 找到 → 获取 customer_id → 新增当前 AppID 的 openid 记录
|
||||
│ │
|
||||
│ └── 没找到 → 创建新客户 + openid 记录
|
||||
│
|
||||
└── NO → 创建新客户 + openid 记录
|
||||
```
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `client-asset-token`:资产验证令牌机制。A1 接口、asset_token JWT 生成/验证、IP 限频、安全规范(不暴露 asset_id)
|
||||
- `client-wechat-login`:微信登录(公众号+小程序)。A2/A3 接口、OAuth/jscode2session 对接、客户查找/创建/合并逻辑、资产绑定(**首次绑定时触发 `asset_status` 从 1→2**)、OpenID 多记录管理
|
||||
- `client-phone-bindng`:手机号绑定/换绑。A4/A5/A6 接口、验证码发送/校验、限频规则、绑定/换绑逻辑
|
||||
- `client-token-management`:有状态 JWT Token 管理。签发、Redis 存储、有效性检查、退出登录(A7)、服务端主动失效
|
||||
- `personal-customer-openid`:PersonalCustomerOpenID 模型定义、唯一索引、与 PersonalCustomer 的关系
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `personal-customer`:PersonalCustomer 模型行为变化——登录逻辑从手机号+验证码改为微信授权,wx_open_id 字段保留但逻辑迁移到 PersonalCustomerOpenID 表
|
||||
- `asset-lifecycle-status`:首次客户绑定资产时,`asset_status` 从 1(在库)自动更新为 2(已销售),使用条件更新确保幂等
|
||||
- `wechat-official-account`:OAuth 配置来源变化——从 YAML 静态配置改为从 WechatConfig 表动态读取公众号/小程序 AppID+AppSecret
|
||||
|
||||
### 微信 SDK 使用说明
|
||||
|
||||
本提案使用项目中已有的微信 SDK(`pkg/wechat/`,基于 PowerWeChat v3),同时需要扩展小程序能力:
|
||||
|
||||
| 场景 | SDK 方法 | 文件 | 状态 |
|
||||
|------|---------|------|------|
|
||||
| A2 公众号登录 | `OfficialAccountService.GetUserInfoDetailed(code)` | `pkg/wechat/official_account.go:69` | ✅ 已有,直接复用 |
|
||||
| A3 小程序登录 | `MiniAppService.Code2Session(code)` | `pkg/wechat/miniapp.go` | ❌ **需新建**,直接 HTTP 调用微信 jscode2session |
|
||||
| SDK 实例创建 | `NewOfficialAccountAppFromConfig(wechatConfig)` | `pkg/wechat/config.go` | ❌ **需新增**,从 DB 动态创建 |
|
||||
| SDK 实例创建 | `NewMiniAppServiceFromConfig(wechatConfig)` | `pkg/wechat/config.go` | ❌ **需新增** |
|
||||
| SDK 实例创建 | `NewPaymentAppFromConfig(wechatConfig, appID)` | `pkg/wechat/config.go` | ❌ **需新增**,供提案 2 支付使用 |
|
||||
|
||||
**现有 `NewOfficialAccountApp(cfg)` 从 YAML 创建实例,客户端场景需要从 `tb_wechat_config` DB 动态加载。**
|
||||
|
||||
## Impact
|
||||
|
||||
- **新增文件**:`internal/model/personal_customer_openid.go`(模型)、`internal/handler/app/client_auth.go`(认证 Handler)、`internal/service/client_auth/service.go`(认证 Service)、`internal/store/postgres/personal_customer_openid_store.go`(Store)、**`pkg/wechat/miniapp.go`(小程序 SDK 封装)**、DTO 文件、迁移文件、常量定义
|
||||
- **修改文件**:`internal/middleware/personal_auth.go`(增加 Redis 检查)、`internal/routes/personal.go`(新增路由)、`internal/bootstrap/`(注册新模块)、`cmd/api/docs.go` + `cmd/gendocs/main.go`(文档生成器)、`pkg/config/defaults/config.yaml`(新增 client 配置节)、`internal/model/system.go`(AutoMigrate 注册新模型)、**`pkg/wechat/config.go`(新增 3 个 DB 动态工厂函数)**、**`pkg/wechat/wechat.go`(新增 MiniAppServiceInterface)**
|
||||
- **新增 API 路由**:`/api/c/v1/auth/` 下 7 个端点
|
||||
- **数据库变更**:新建 `tb_personal_customer_openid` 表
|
||||
- **新增依赖**:无(微信 SDK 已有 PowerWeChat v3,小程序 jscode2session 为纯 HTTP 调用)
|
||||
- **配置变更**:config.yaml 新增 `client.require_phone_binding` 配置项
|
||||
Reference in New Issue
Block a user