Skip to content

登录注册体系

状态:现状梳理 + 微信登录三端接入(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 编译。它只能依赖稳定的接口(ILoginServiceWxFactoryICloudPortService 等),具体实现类由承载它的 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-pwd
  • hiapi-fast-frame/hiapi-fast-core/hiapi-core-login/ —— ILoginServiceLoginTypeAccountType、各种 Request/Response DTO
  • hiapi-fast-frame/hiapi-fast-core/hiapi-core-dispatch/ —— DispatchContext(按接口类型查 bean)、ICloudPortServiceCloudConst(含 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 → 返回新建的 User

WxUserInfounionid 优先查、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 实现:

账户类型实现类所在仓库
USERUserAccountServicehiapi-cloud-user
Merchant商户后台账号hiapi-cloud-user(未在本文档展开)
Platform平台运营账号hiapi-cloud-user(未在本文档展开)
Channel渠道商账号hiapi-cloud-user(未在本文档展开)

六、涉及仓库/文件速查

仓库关键文件
hiapi-fast-framehiapi-core-login/.../LoginType.javahiapi-core-thirdparty-api/hiapi-core-thirdparty-server/(微信SDK)、hiapi-core-dispatch/.../CloudConst.java(PORT_TYPE_*)
hiapi-cloud-private/hiapi-core-publicAccountController.java(受保护模块,改完需 report-public-module 重新上报)
hiapi-cloud-userUserAccountService.java(登录/注册落库)、WxUserInfo.java + WxUserInfoService.java(微信绑定表)
hiapi-cloud-publicPlatPortService.java(ICloudPortService 实现,读 hiapi_core_system_port)
hiapi-cloud-admin-tssrc/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-userUserAccountService),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-framehiapi-core-login/.../request/ForgetPwd.javasenderToken 字段
hiapi-fast-framehiapi-core-login/.../request/BasicLoginRequest.javaemail 字段
hiapi-fast-framehiapi-core-language/.../LanguageCore.javaREGISTER_EMAIL_EXISTS/LOGIN_EMAIL_NOT_EXISTS/RESET_PWD_TOKEN_INVALID
hiapi-cloud-private/hiapi-core-publiclogin/api/AccountController.javaregister() 验证码校验条件扩到 EMAIL 类型(受保护模块,已 report-public-module 上报,见下方部署记录)
hiapi-cloud-userhiapi-user-service/.../UserAccountService.java实现 forget()/resetPwd(),register() 加 EMAIL case
hiapi-cloud-userhiapi-user-entity/.../UserLogs.javaACTION_FORGET_PWD/ACTION_RESET_PWD
hiapi-cloud-userhiapi-user-service/pom.xmlhiapi-core-sender-feign(用 FeignSender)、hiapi-core-redis(存 resetToken)依赖

Feign 通路(FeignUserAccountControllerIUserAccountServicehiapi-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 → 拿 resetTokenPOST /account/reset-pwd → 用新密码登录。