UniApp 多端登录体系设计:微信、手机号与账号互通
结论先行:多端账号互通的关键是把"身份凭据"与"账号主体"分离:每个微信 openid 只是一条凭据,真正的账号主体靠 user 表唯一标识,凭据通过 unionid 或手机号归拢到同一账号。落地就是两张表——user 存账号主体,user_auth 存各端凭据;登录走三条链路(微信授权、手机号验证、账号密码),token 用双令牌(access + refresh)静默续期。这套模型能支撑小程序、H5、APP 三端同一用户同一份数据。
openid、unionid 与统一账号:先理顺三个概念
用一个类比开篇:openid 像"某家店的会员卡号"——你在奶茶店的卡号和你在隔壁面馆的卡号互不相同;unionid 像"同一家连锁集团的统一会员号"——只要奶茶店和面馆都注册在同一个微信开放平台账号(同主体)下,微信就能用同一个 unionid 认出你是同一个人。
三者的规则:
- openid:同一用户在不同微信应用(小程序、公众号、APP 内微信登录)中的 openid 各不相同。
- unionid:前提是各应用绑定在同一个微信开放平台账号下,同一用户的 unionid 一致;未绑定开放平台的小程序拿不到 unionid。
- 手机号:天然跨端、跨微信的强凭据,是账号互通最可靠的锚点。
所以工程结论是:unionid 负责微信体系内的自动归拢,手机号负责微信体系外的最终兜底,两者缺一不可。
数据模型:user 与 user_auth 两张表
新手最常犯的错误是把 openid 直接写在 user 表里——一加 APP 端就得改表。正确姿势是拆两张表:
-- 账号主体表:一个自然人一条记录
CREATE TABLE user (
id BIGINT PRIMARY KEY,
nickname VARCHAR(64),
avatar VARCHAR(255),
mobile VARCHAR(20) UNIQUE, -- 可为空,登录后引导绑定
created_at DATETIME
);
-- 凭据表:每端每渠道一条记录,user_id 指向主体
CREATE TABLE user_auth (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL, -- 指向 user.id
provider VARCHAR(20) NOT NULL, -- weixin_mp / weixin_app / mobile / password
openid VARCHAR(64), -- 各端 openid
unionid VARCHAR(64), -- 微信体系内归拢用
credential VARCHAR(255) -- 密码哈希等,仅 password 类型使用
);登录匹配规则固定为三级:先用 unionid 找账号;找不到再按 provider+openid 找;都没有就新建 user 并落一条 user_auth。手机号绑定时若发现该手机号已有账号,就进入账号合并流程(后文讲)。
三条登录链路的实现要点
- 微信登录(小程序/APP):前端
uni.login拿 code → 服务端换 openid/unionid → 按上述规则匹配或建号 → 签发 token。APP 端微信登录同样要求应用在微信开放平台注册并配置 Universal Links(iOS)。 - 手机号登录:小程序端可用手机号快捷组件(需企业主体认证,每次调用计费,以官方说明为准);H5 与 APP 走短信验证码,注意验证码防刷(图形验证前置+频率限制)。
- 账号密码:作为兜底链路,密码只存 bcrypt/argon2 哈希,绝不明文。
三端前端封装建议收敛成一个 authStore:启动时先取本地 token 静默续期,失败再静默微信登录,仍未登录则由页面按需触发登录引导,避免每个页面各写一套登录逻辑。
token 设计与静默续期
推荐双令牌方案:access token 有效期短(如 2 小时),refresh token 有效期长(如 30 天)且仅用于换新 access token。要点四个:
- 无状态与可吊销的平衡:access token 用 JWT(JSON Web Token)自验签,不加查库开销;refresh token 存服务端(Redis),支持踢人下线与主动失效。
- 静默续期:请求拦截器捕获 401 后先调 refresh 接口换新令牌并重放原请求;并发请求要合并续期(加锁),避免风暴。
- 小程序会话对齐:
uni.checkSession检查微信侧会话是否过期,过期则重新uni.login,这与自建 token 的过期是两回事,要分别处理。 - 存储安全:token 存本地缓存并配合传输层 HTTPS;不要把敏感信息塞进 JWT 载荷,它是可解码的。
账号合并与换绑:容易漏的边界
多凭据体系必然遇到冲突:用户先用微信登录建了账号 A,后来又用同一手机号验证码登录建了账号 B,或者想给账号换绑新手机号。处理原则三条:
- 合并要走确认:检测到可归拢的凭据冲突时,让用户确认合并哪个身份,订单、余额等资产数据按业务规则迁移,合并操作记录审计日志。
- 换绑要验证旧凭据:换手机号必须先校验旧手机验证码(或已登录状态加人脸等强因子),防止账号被恶意换绑盗走。
- unionid 只归拢不自动合并资产:微信侧的自动归拢适合"识别同一人",涉及资金的合并永远加人工确认环节。
以猫拽低代码(CatPull)为例,这类多端账号体系在低代码平台里被抽象成标准能力:基于 UniApp 技术栈的 DSL 编译到小程序、H5 与 APP,平台层提供统一的用户模型与权限配置,开发者通过可视化配置声明登录方式与字段映射,不必每个项目重写一遍 authStore;需要定制时再在工作流引擎中挂自定义云函数处理合并等特殊逻辑。
FAQs
Q1:没有绑定微信开放平台,多端微信登录还能互通吗?
A:微信体系内无法靠 unionid 自动互通,只能靠手机号兜底:让用户在小程序里授权手机号,APP 端登录后绑定同一手机号,即可归拢到同一账号。建议尽早完成开放平台绑定,unionid 体验最顺。
Q2:手机号一键登录每次都收费吗?
A:小程序手机号快捷授权按调用计费(具体单价以微信官方文档为准);APP 端运营商一键登录按成功取号计费,各家运营商价格不同。短信验证码本身也有通道成本,高频场景要做防刷。
Q3:JWT 被盗了怎么办?
A:双令牌结构下,服务端吊销 refresh token 即可阻断续期,access token 等短有效期自然过期。高风险操作(改绑、支付)应要求二次验证,不能只依赖 token 本身。
