Appearance
登录注册体系
状态:现状梳理 + 微信登录三端接入(2026-07-22)+ 找回密码/邮箱注册已实现(2026-07-23) | 范围:四账户类型(platform/channel/merchant/user)统一登录入口,代码横跨
hiapi-fast-frame(登录框架抽象)、hiapi-cloud-private/hiapi-core-public(受保护模块,唯一对外入口)、hiapi-cloud-user(USER 账户类型的落库实现)。
一、整体架构
登录注册没有按账户类型拆成四套接口,而是一个入口 + 按账户类型分发:
前端 → POST /account/login|/register (hiapi-core-public, 受保护模块, 经 nuwa 下发)
│
├─ AccountController 做通用前置处理:验证码校验、加锁防并发、微信 code 换 openid
│
├─ LoginContext.getAccountType(accountType) 拿到对应 ILoginService 实现
│ USER → UserAccountService (hiapi-cloud-user)
│ Merchant / Platform / Channel → 各自的 XxxAccountService (未在本文档展开)
│
├─ ILoginService.login()/register() 做账户类型特有的业务逻辑(查库/建号)
│
└─ AccountController 收尾:ITokenCreateService 建 Token、写 Cookie、返回 token 字符串为什么入口是"受保护模块":hiapi-core-public 经 ProGuard 混淆后由 nuwa 中央模块库在运行时下发,不直接参与常规 Maven reactor 编译。它只能依赖稳定的接口(ILoginService、WxFactory、ICloudPortService 等),具体实现类由承载它的 app 模块(如 hiapi-cloud-public-app)在 classpath 里提供,通过 DispatchContext.getServiceOne(接口.class) 按类型查 Spring 容器拿 bean。改这个模块后必须走 report-public-module skill 重新构建 + 上报,不会随普通 mvn install 生效。
关键文件:
hiapi-cloud-private/hiapi-core-public/src/main/java/cn/hiapi/core/login/api/AccountController.java—— 唯一入口,/account/register、/account/login、/account/get_info、/account/logout、/account/edit-pwd、/account/forget、/account/reset-pwdhiapi-fast-frame/hiapi-fast-core/hiapi-core-login/——ILoginService、LoginType、AccountType、各种 Request/Response DTOhiapi-fast-frame/hiapi-fast-core/hiapi-core-dispatch/——DispatchContext(按接口类型查 bean)、ICloudPortService、CloudConst(含PORT_TYPE_*常量)
二、Token 创建与下发(所有登录方式公用)
AccountController.login() 里,不管走的是账号密码、短信验证码还是微信登录,只要 ILoginService.login() 返回成功,后面的建 Token 逻辑是同一段代码(不在任何 if 分支里):
java
Token token = dispatchContext.getServiceOne(ITokenCreateService.class).create(Token.builder()
.mid(account.getMid()).fid(account.getFid())
.accountType(account.getAccountType())
.channel(login.getChannel())
.loginType(login.getType().name())
.authority(...).expire(...).data(...)
.build());
httpServletResponse.addCookie(new Cookie("hiapi-token-" + accountType, token.getToken()));同时写 hiapi-mid(商户ID,AES加密)、hiapi-dfp(设备指纹随机值)两个辅助 Cookie。Token 结构里没有独立的"平台端口"字段,channel 字段(H5/小程序/公众号/APP,前端自己传)起到区分登录终端的作用。
三、登录方式(LoginType)
hiapi-fast-frame/hiapi-fast-core/hiapi-core-login/.../enums/LoginType.java:
| 值 | 说明 | 承载账户类型 |
|---|---|---|
ACCOUNT | 账号密码 | 全部 |
EMAIL | 邮箱注册 + 找回密码身份校验(2026-07-23 新增)。尚未支持邮箱登录——UserAccountService.login() 的 switch 没有 EMAIL 分支,只有 register() 和 forget() 接了 | USER |
MOBILE_SMS | 短信验证码登录/注册;手机号不存在时登录直接报错(LOGIN_MOBILE_NOT_EXISTS),不会自动注册——产品决定保持这个行为,不要在不确认的情况下"顺手改成自动注册" | 全部 |
WX_PROG | 微信小程序 | USER |
WX_PUB | 微信公众号网页授权 | USER |
WX_APP | 微信开放平台 APP 授权登录 | USER(2026-07-22 新增) |
WX_PROG_MOBILE | 小程序一键手机号 | 预留,未接 |
目前微信三种登录方式只对 USER 账户类型开放(UserAccountService),Merchant/Platform/Channel 后台账号不支持微信登录。
四、微信登录三端接入(小程序 / 公众号 / APP)
4.1 请求参数约定(三端一致)
json
POST /account/login
{
"accountType": "USER",
"channel": "MP_WEIXIN | H5 | APP",
"type": "WX_PROG | WX_PUB | WX_APP",
"data": { "code": "微信授权code(必填)", "appId": "对应端口的appId(选填,不传取该商户该类型下默认端口)" }
}三端参数结构完全一样,区别只在 type 决定去查哪种 PlatPort。前端(小程序 wx.login()、公众号网页授权跳转、APP 微信开放平台SDK)各自拿到 code 后按上面结构调 /account/login 即可 —— 这部分前端代码目前未实现,见第七节。
4.2 后端处理链路
AccountController.resolveWxLogin(mid, type, data)
│
├─ 按 type 映射端口类型: WX_PROG→wx_mini_program WX_PUB→wx_official_account WX_APP→wx_app
├─ ICloudPortService.getCloudPort(mid, portType, appId) 查 PlatPort(hiapi_core_system_port 表)
│ 拿不到配置 → 报错"平台还未配置微信参数"
├─ WxFactory.newService(portConfig) 按 type 生产 IWxService 实现(hiapi-core-thirdparty-server)
│ wx_mini_program → WxProgramService(走 jscode2session)
│ wx_official_account / wx_app → WxMpService(走 oauth2,两者接口完全一致,复用同一实现)
├─ IWxService.getUserInfo(code) 换 openid/unionid(+snsapi_userinfo授权时的昵称/头像)
│ 显式只回填 openid/unionid/type/appId/nickname/avatar 到 data —— session_key/access_token
│ 等敏感字段换完即弃,不透传、不落库、不进日志
└─ data 写回后交给 ILoginService.login() 继续走通用登录流程4.3 首次登录 = 自动注册(UserAccountService.wxLoginOrRegister)
按 mid + appId + openid/unionid 查 WxUserInfo 绑定表(hiapi_core_user_wx_info)
├─ 查到 → 直接返回已绑定的 User,走正常登录
└─ 查不到 → 新建 User → 建推荐关系链(邀请码可选)→ 建 WxUserInfo 绑定记录
→ 建 UserInfo 扩展资料 → 发 UserRegisterEvent → 返回新建的 UserWxUserInfo 按 unionid 优先查、openid 兜底 —— 只要商户在微信开放平台把小程序/公众号/APP 三个应用绑定到同一个开放平台账号下,三端登录会因为 unionid 一致自动合并成同一个用户,不需要额外的账号合并逻辑。
4.4 PlatPort:三端配置的落位
微信小程序/公众号/APP(以及支付宝小程序)的 appId/appSecret 统一存在 PlatPort 表(hiapi_core_system_port,按 mid + type + appId 唯一),后台管理页面 hiapi-cloud-admin-ts/src/views/system/config/port/index.vue 已经支持全部四种类型的增删改,本次没有新增页面。
4.5 本次重写的原因
IWxService/AbsWxService 之前是一套自研的最简 HTTP 封装,只覆盖了小程序一种登录方式,且有几个实际问题:用全局 120s 超时的 HTTP 客户端拖住登录请求线程、errcode==0 判成功依赖 fastjson 对缺失字段的隐式默认值、appSecret 拼在 GET 请求 URL 里被完整打进日志/异常信息、微信原始响应(含小程序 session_key)被整体 putAll 进登录请求 DTO 有敏感信息泄露风险、有一张全仓库零引用的死代码表(AccessToken/hiapi_core_thirdparty_access_token,已删除)。这次重写沿用自研 HTTP 方案(不引入第三方微信 SDK),但补齐了超时收紧、显式错误码判断、错误信息脱敏、配置前置校验、常见微信错误码中文友好提示。
五、账户类型体系(简述)
AccountType 常量定义了四种账户类型,LoginContext.getAccountType(accountType) 按字符串路由到对应 ILoginService 实现:
| 账户类型 | 实现类 | 所在仓库 |
|---|---|---|
USER | UserAccountService | hiapi-cloud-user |
Merchant | 商户后台账号 | hiapi-cloud-user(未在本文档展开) |
Platform | 平台运营账号 | hiapi-cloud-user(未在本文档展开) |
Channel | 渠道商账号 | hiapi-cloud-user(未在本文档展开) |
六、涉及仓库/文件速查
| 仓库 | 关键文件 |
|---|---|
hiapi-fast-frame | hiapi-core-login/.../LoginType.java、hiapi-core-thirdparty-api/、hiapi-core-thirdparty-server/(微信SDK)、hiapi-core-dispatch/.../CloudConst.java(PORT_TYPE_*) |
hiapi-cloud-private/hiapi-core-public | AccountController.java(受保护模块,改完需 report-public-module 重新上报) |
hiapi-cloud-user | UserAccountService.java(登录/注册落库)、WxUserInfo.java + WxUserInfoService.java(微信绑定表) |
hiapi-cloud-public | PlatPortService.java(ICloudPortService 实现,读 hiapi_core_system_port) |
hiapi-cloud-admin-ts | src/views/system/config/port/index.vue(端口配置后台页) |
七、已知限制 / 后续 TODO
- 前端未接:小程序端
wx.login()调用、公众号网页授权跳转页、以及uniapp/hiapi-cloud-uniapp的登录页(src/pages/auth/login.vue,2026-06-18 因迁移到hiapi-cloud-public-web被删除)目前均未实现,只有后端 API 就绪。 - APP 端没有原生工程:workspace 内没有独立的 Android/iOS 原生项目,
WX_APP目前只能做到"后端接口 + PlatPort 配置就绪",接入需要等原生 APP 项目落地后由客户端发起code换取。 - Merchant/Platform/Channel 三种账户类型的登录实现未展开梳理,后续如有需要按本文档结构补充。
- 找回密码/邮箱注册前端未接:见第八节,后端 API 已就绪,
hiapi-cloud-admin-ts/hiapi-cloud-uniapp都还没有调用这两个接口的页面。
八、找回密码 / 邮箱注册(2026-07-23 新增)
AccountController.forget()/resetPwd() 原来是平台级空白(所有账户类型的 ILoginService 实现都是 throw new BasicException(...) 或直接 return null),这次只补了 USER 账户类型(hiapi-cloud-user 的 UserAccountService),Merchant/Platform/Channel 三种账户类型依然是空白,需要时照同一套模式补。
8.1 找回密码分两步,复用验证码一次性核销机制
POST /account/forget { type: MOBILE_SMS|EMAIL, account, code, senderToken, countryCode?, accountType: USER }
→ UserAccountService.forget():
1. 按 type 查用户(getByMobile / getByEmail)
2. FeignSender.verify(mid, senderToken, code, countryCode, account) 校验验证码(一次性核销)
3. 生成随机 resetToken,写 Redis「user:reset-pwd:{resetToken}」→「mid:uid」,10 分钟有效
4. 返回 resetToken(ResponseEntity<String>)
POST /account/reset-pwd { token: <resetToken>, password: <新密码>, accountType: USER }
→ UserAccountService.resetPwd():
1. 查 Redis 拿 mid:uid,查到即删(一次性)
2. 新密码格式校验(8-20位字母+数字,与 update-password 同一条正则)
3. 用该用户的 iv 做 AES 加密写库,清缓存关键改动:共享 DTO ForgetPwd(hiapi-fast-core/hiapi-core-login)原来没有 senderToken 字段,导致没法走 SenderFactory.verify 的 token+code 配对校验——这次给它加了这个字段(向后兼容的新增字段)。ResetPwd 本来就有 token 字段(注释写的是"验证码"容易误读,实际语义是"找回密码第一步返回的一次性 reset token",不要跟短信/邮箱验证码的 code/senderToken 搞混)。
8.2 邮箱注册
BasicLoginRequest(RegisterRequest/LoginRequest 共同基类)加了 email 字段。AccountController.register() 里原来只有 LoginType.MOBILE_SMS 才会走 SenderFactory.verify 校验验证码,现在 LoginType.EMAIL 也会走(to 参数改传 register.getEmail())。UserAccountService.register() 新增 EMAIL case,与 MOBILE_SMS 对称:查重(REGISTER_EMAIL_EXISTS)→ 建号(默认昵称"邮箱用户",没有做邮箱掩码工具,不像手机号那样脱敏显示)。
8.3 涉及文件
| 仓库 | 文件 | 改动 |
|---|---|---|
hiapi-fast-frame | hiapi-core-login/.../request/ForgetPwd.java | 加 senderToken 字段 |
hiapi-fast-frame | hiapi-core-login/.../request/BasicLoginRequest.java | 加 email 字段 |
hiapi-fast-frame | hiapi-core-language/.../LanguageCore.java | 加 REGISTER_EMAIL_EXISTS/LOGIN_EMAIL_NOT_EXISTS/RESET_PWD_TOKEN_INVALID |
hiapi-cloud-private/hiapi-core-public | login/api/AccountController.java | register() 验证码校验条件扩到 EMAIL 类型(受保护模块,已 report-public-module 上报,见下方部署记录) |
hiapi-cloud-user | hiapi-user-service/.../UserAccountService.java | 实现 forget()/resetPwd(),register() 加 EMAIL case |
hiapi-cloud-user | hiapi-user-entity/.../UserLogs.java | 加 ACTION_FORGET_PWD/ACTION_RESET_PWD |
hiapi-cloud-user | hiapi-user-service/pom.xml | 补 hiapi-core-sender-feign(用 FeignSender)、hiapi-core-redis(存 resetToken)依赖 |
Feign 通路(FeignUserAccountController → IUserAccountService → hiapi-cloud-public-app 的代理 UserAccountService)本来就是通的,这次没有新开接口,只是把两个 return null 换成了真实实现。
8.4 部署记录
2026-07-23 已跑 report-public-module 上报 hiapi-core-public:先上报了 ProGuard 混淆版(auto-20260723-121001),用户要求改传未混淆版,又上报了一次(auto-20260723-121216,92597 字节)。两个版本目前都在 nuwa stable 通道里,按"取最新"应该是未混淆版生效,但没有验证 nuwa 是否真的有清理/覆盖旧版本的机制——如果后续发现线上还是混淆版在跑,先去查这一点。
未做端到端验证(没有可跑的部署环境),建议部署后手动跑一遍 POST /account/forget → 拿 resetToken → POST /account/reset-pwd → 用新密码登录。