C 端认证系统功能总结
概述
本次实现了面向个人客户(C 端)的完整认证体系,替代旧 H5 登录接口。支持微信公众号和小程序两种登录方式,基于「资产标识符验证 → 微信授权 → 自动绑定资产 → 可选绑定手机号」的流程。
接口列表
| 接口 |
路径 |
认证 |
说明 |
| A1 |
POST /api/c/v1/auth/verify-asset |
否 |
资产标识符验证,返回 asset_token |
| A2 |
POST /api/c/v1/auth/wechat-login |
否 |
微信公众号登录 |
| A3 |
POST /api/c/v1/auth/miniapp-login |
否 |
微信小程序登录 |
| A4 |
POST /api/c/v1/auth/send-code |
否 |
发送手机验证码 |
| A5 |
POST /api/c/v1/auth/bind-phone |
是 |
首次绑定手机号 |
| A6 |
POST /api/c/v1/auth/change-phone |
是 |
换绑手机号(双验证码) |
| A7 |
POST /api/c/v1/auth/logout |
是 |
退出登录 |
登录流程
核心设计
有状态 JWT(JWT + Redis)
- JWT payload 仅含
customer_id + exp
- 登录时将 token 写入 Redis,TTL 与 JWT 一致
- 每次请求在中间件同时校验 JWT 签名和 Redis 有效状态
- 支持服务端主动失效(封禁、强制下线、退出登录)
- 单点登录:新登录覆盖旧 token
OpenID 多记录管理
- 新增
tb_personal_customer_openid 表
- 同一客户可在多个 AppID(公众号/小程序)下拥有不同 OpenID
- 唯一约束:
UNIQUE(app_id, open_id) WHERE deleted_at IS NULL
- 客户查找逻辑:openid 精确匹配 → unionid 回退合并 → 创建新客户
资产绑定
- 每次登录创建
PersonalCustomerDevice 绑定记录
- 同一资产允许被多个客户绑定(支持转手场景)
- 首次绑定时自动将资产状态从「在库(1)」更新为「已销售(2)」
微信配置动态加载
- 登录时从数据库
tb_wechat_config 动态读取激活配置
- 优先走 WechatConfigService 的 Redis 缓存
- 小程序登录直接 HTTP 调用微信
jscode2session(不依赖 PowerWeChat SDK)
限流策略
| 接口 |
维度 |
限制 |
| A1 |
IP |
30 次/分钟 |
| A4 |
手机号 |
60 秒冷却 |
| A4 |
IP |
20 次/小时 |
| A4 |
手机号 |
10 次/天 |
新增/修改文件
新增文件
| 文件 |
说明 |
internal/model/personal_customer_openid.go |
OpenID 关联模型 |
internal/model/dto/client_auth_dto.go |
A1-A7 请求/响应 DTO |
internal/store/postgres/personal_customer_openid_store.go |
OpenID Store |
internal/service/client_auth/service.go |
认证 Service(核心业务逻辑) |
internal/handler/app/client_auth.go |
认证 Handler(7 个端点) |
pkg/wechat/miniapp.go |
小程序 SDK 封装 |
migrations/000083_add_personal_customer_openid.up.sql |
迁移文件 |
migrations/000083_add_personal_customer_openid.down.sql |
回滚文件 |
修改文件
| 文件 |
说明 |
internal/middleware/personal_auth.go |
增加 Redis 双重校验 |
pkg/constants/redis.go |
新增 token 和限流 Redis Key |
pkg/errors/codes.go |
新增错误码 1180-1186 |
pkg/config/defaults/config.yaml |
新增 client.require_phone_binding |
pkg/wechat/wechat.go |
新增 MiniAppServiceInterface |
pkg/wechat/config.go |
新增 3 个 DB 动态工厂函数 |
internal/bootstrap/types.go |
新增 ClientAuth Handler 字段 |
internal/bootstrap/handlers.go |
实例化 ClientAuth Handler |
internal/bootstrap/services.go |
初始化 ClientAuth Service |
internal/bootstrap/stores.go |
初始化 OpenID Store |
internal/routes/personal.go |
注册 7 个认证端点 |
cmd/api/docs.go |
注册文档生成器 |
cmd/gendocs/main.go |
注册文档生成器 |
错误码
| 码值 |
常量名 |
说明 |
| 1180 |
CodeAssetNotFound |
资产不存在 |
| 1181 |
CodeWechatConfigUnavailable |
微信配置不可用 |
| 1182 |
CodeSmsSendFailed |
短信发送失败 |
| 1183 |
CodeVerificationCodeInvalid |
验证码错误或已过期 |
| 1184 |
CodePhoneAlreadyBound |
手机号已被其他客户绑定 |
| 1185 |
CodeAlreadyBoundPhone |
已绑定手机号不可重复绑定 |
| 1186 |
CodeOldPhoneMismatch |
旧手机号与当前绑定不匹配 |
数据库变更
- 新建表
tb_personal_customer_openid(迁移 000083)
- 唯一索引:
idx_pco_app_id_open_id (app_id, open_id) 软删除条件
- 普通索引:
idx_pco_customer_id (customer_id)
- 条件索引:
idx_pco_union_id (union_id) WHERE union_id != ''
配置项
| 配置路径 |
环境变量 |
默认值 |
说明 |
client.require_phone_binding |
JUNHONG_CLIENT_REQUIRE_PHONE_BINDING |
true |
是否要求绑定手机号 |