移动端登录 · 使用与配置文档
面向 站点管理员(Halo 站长)。App / 小程序端开发者请看 《移动端登录 · 接口文档》。
适用插件版本:uni-halo v3.0.0+,Halo:2.26.0+
一、这个能力解决什么问题
Halo 原生没有「第三方客户端登录」能力。Halo 的访问令牌(PAT)需要用户在后台手动创建,App 端无法自助获取。
本插件补上这一环:App 端调用插件接口完成登录,插件代用户签发一枚 Halo 原生 PAT 返回。该令牌与后台手动创建的令牌完全同构,可访问 Halo 原生 API 与插件接口。
站长需要决定的只有三件事:
- 开不开、开哪几种登录方式
- 登录后给移动端什么权限
- 微信小程序的密钥(如果要开微信登录)
二、前置条件
| 项 | 要求 |
|---|---|
| Halo 版本 | 2.26.0 及以上(依赖 CryptoService 提供的 PAT 签名能力) |
| 插件版本 | uni-halo v3.0.0+ |
| HTTPS | 强烈建议。令牌以 Bearer 明文传输,HTTP 下等同公开密码 |
| 微信小程序 | 仅在使用微信登录时需要:已注册小程序、拿到 AppID 与 AppSecret |
Halo 版本不足时,登录能力会自动关闭。
三、快速开始
第 1 步:开启登录能力
进入 Halo 后台 → 插件 → UniHalo → 设置 → 移动端登录。
| 配置项 | 说明 | 建议 |
|---|---|---|
| 开启登录能力 | 总开关 | 先设为「开启」,其余配好后再回来确认 |
| 账号密码登录 | 使用 Halo 账号密码登录 | 建议开启 |
| 微信小程序一键登录 | 微信登录开关 | 需要微信小程序,否则关闭 |
| 自动注册用户名前缀 | 微信新用户的用户名前缀 | 默认 unihalo,见下方说明 |
密码登录与微信登录是两条互相独立的能力,各自有开关,不存在从属关系。 微信一键登录固定为「已绑定则登录、未绑定则自动建号并登录」,没有「必须先注册」的中间态。
「自动注册用户名前缀」怎么设?
首次登录的微信用户会自动创建 Halo 账号,用户名 = 前缀 + 两位递增序号:
unihalo01 unihalo02 unihalo03 ... unihalo99 unihalo100| 约束 | 说明 |
|---|---|
| 字符 | 只允许小写字母、数字、连字符;不能以连字符开头或结尾 |
| 长度 | 前缀 + 两位序号后总长度需在 4–63 之间(Halo 限制),故前缀至少 2 位,建议 ≥3 |
| 不可改 | Halo 用户名创建后不可修改,请一次设好 |
配套昵称同样带序号(微信用户01),便于在用户列表里区分。初始密码为随机强密码且不对外提供,这类账号通过微信登录使用。
前缀非法时会自动回落到
unihalo并在日志中告警,注册功能不会中断。
第 2 步:配置登录权限
同页 登录权限 区域。
| 配置项 | 说明 | 默认 |
|---|---|---|
| 权限策略 | 继承用户在 Halo 的角色 或 按下方配置裁剪 | 继承 |
| 登录后默认角色 | 裁剪模式下勾选的角色 | 已登录用户 |
| 令牌有效期(天) | 登录令牌的有效天数 | 30 |
两种策略怎么选?
| 策略 | 行为 | 适用 |
|---|---|---|
| 继承(inherit) | 令牌权限 = 该用户在 Halo 的全部角色权限 | 管理员自己用、信任的移动端;希望移动端与网页端权限一致 |
| 裁剪(intersect) | 令牌权限 = 勾选角色 ∩ 用户已有角色 | 公开站点,希望统一压低移动端权限上限 |
关键:两种策略都不会提权。
裁剪模式是取交集,不是「赋予」。勾选「管理员」但用户本身是「投稿者」→ 最终只有投稿者权限。这保证移动端永远无法获得超过用户本人的权限。
裁剪模式的常见坑
自动注册的微信新用户在 Halo 里几乎没有角色,交集很可能是空 → 登录后零权限,permissions 为 []。
解决办法二选一:
- 在「登录后默认角色」中保留「已登录用户」(
authenticated) —— 这是 Halo 给所有登录用户的基础角色,新用户也有 - 或改用「继承」策略
「已登录用户」角色自带:读取自己资料、修改自己昵称/简介、上传头像、修改密码、管理自己的令牌等。用它兜底即可满足大多数移动端场景。
第 3 步:配置微信小程序密钥(仅微信登录需要)
- 先开启「微信小程序一键登录」
- 点击 微信小程序密钥 选择器 → 新建密钥
- 填入两项:
| Key | 值 | 获取位置 |
|---|---|---|
appId | 小程序 AppID | 微信公众平台 → 开发管理 → 开发设置 |
appSecret | 小程序 AppSecret | 同上(需管理员扫码,只显示一次,请立即保存) |
- 保存设置
密钥只保存在服务端,不会下发给客户端;请妥善保管 AppSecret,泄露后请在微信公众平台重置并回到此处更新。
结论:安全边界是「Halo 的权限体系」,不是加密。因此不要给不可信的角色授予 Secret 读取权限,也不要把 Halo 管理员账号交给不信任的人。
第 4 步:配置小程序服务器域名
在 微信公众平台 → 开发管理 → 开发设置 → 服务器域名 → request 合法域名 中加入你的站点域名:
https://example.com必须是已备案域名 + 有效 HTTPS 证书,且不能带端口。微信小程序不支持 IP 和自定义端口。
第 5 步:验证
- 用 App / 小程序访问
GET /apis/api.unihalo.ialley.cn/v1alpha1/getConfigs/loginConfig,确认返回的三个开关与设置一致 - 走一次登录,确认能拿到
token - 带
Authorization: Bearer <token>调一次GET .../auth/profile,确认能拿到用户信息
四、配置项速查表
| 分组 | 配置项 | 类型 | 默认 | 是否下发客户端 |
|---|---|---|---|---|
| 登录方式 | 开启登录能力 | 单选 | 关闭 | 是 |
| 登录方式 | 账号密码登录 | 开关 | 开 | 是 |
| 登录方式 | 微信小程序一键登录 | 开关 | 关 | 是 |
| 登录方式 | 微信小程序密钥 | Secret | 空 | 否 |
| 登录方式 | 自动注册用户名前缀 | 文本 | unihalo | 否 |
| 登录权限 | 权限策略 | 下拉 | 继承 | 否 |
| 登录权限 | 登录后默认角色 | 多选 | 已登录用户 | 否 |
| 登录权限 | 令牌有效期(天) | 数字 | 30 | 否 |
五、令牌管理
5.1 令牌长什么样
- 插件签发的令牌以
pat_开头,与你在 Halo 后台手动创建的个人令牌同等地位,可直接访问 Halo 原生 API 与插件全部接口
5.2 在哪里能看到
Halo 后台 → 个人中心 → 个人令牌。插件签发的令牌与用户手动创建的混在一起展示,可通过名称区分:插件签发的形如 pat-<用户名>-<随机>。
5.3 过期与清理
| 机制 | 说明 |
|---|---|
| 自动失效 | 到达「令牌有效期」后自动失效,需重新登录 |
| 定时清理 | 插件会定期清理插件签发的已过期令牌 |
| 安全边界 | 绝不删除用户在后台手动创建的任何令牌 |
想立刻让某个令牌失效:在「个人令牌」页面删除,或让用户在 App 端登出(登出 = 吊销)。
5.4 手动吊销
用户在 App 端调 /auth/-/logout 即吊销当前令牌。管理员也可在后台「个人令牌」直接删除。
六、安全建议
6.1 必须做
- 启用 HTTPS。否则令牌等同明文密码,中间人可直接劫持
- 裁剪模式保留
authenticated,避免新用户零权限(见第 2 步的坑) - 确认「登录权限」里的默认角色包含
authenticated,微信新用户零权限将无法使用任何功能
6.2 强烈建议
- 限制管理员数量。Secret 的保护依赖角色权限体系,管理员越多密钥暴露面越大
- 定期轮换 AppSecret。在微信公众平台重置后,同步更新 Halo 里的 Secret
- 公开站点用裁剪模式,把移动端权限压到最低可用集
- 令牌有效期别设太长。30 天是平衡点;高安全场景建议 7 天
6.3 已知风险(当前版本)
| 风险 | 说明 | 缓解 |
|---|---|---|
| 签名私钥可读 | Halo 的 CryptoService 会返回含私钥的 JWK,理论上插件可伪造任意用户令牌 | 只安装可信插件;不要给不可信插件授权 |
| 限流是单实例内存计数 | 多实例部署时各算各的,且重启后计数清零 | Halo 通常单实例部署;多实例场景请在网关层再叠加一层限流 |
6.4 内置的登录限流
密码登录已加入失败限流,防止暴力破解:
| 维度 | 阈值 | 说明 |
|---|---|---|
| 用户名 | 连续失败 5 次 | 拦定向爆破;登录成功后立即清零 |
| 来源 IP | 累计失败 20 次 | 拦换着用户名扫号;刻意不在成功登录后清零 |
命中后锁定 15 分钟,接口返回 429 TOO_MANY_ATTEMPTS。窗口结束后计数自然作废,无需人工解锁。
IP 取的是 TCP 源地址,不是
X-Forwarded-For—— 后者由客户端提供、可随意伪造,拿它做限流等于没做。反代部署时计到的是反代 IP,这是刻意的取舍。
七、故障排查
| 现象 | 可能原因 | 处理 |
|---|---|---|
所有登录返回 LOGIN_DISABLED | 总开关未开 | 设置页开启「开启登录能力」 |
密码登录返回 PASSWORD_LOGIN_DISABLED | 密码登录开关关了 | 打开开关 |
密码正确却返回 BAD_CREDENTIALS | ① 用了昵称而非用户名 ② 账号开启了 2FA | 用 Halo 用户名登录;2FA 账号改微信登录 |
微信登录返回 WECHAT_LOGIN_FAILED | ① code 已用过 ② AppSecret 错误 ③ 域名未配置 | 重新 wx.login();核对 Secret;检查小程序服务器域名 |
微信登录返回 REGISTER_FAILED | 序号连续被占用(极端并发) | 重试一次即可;持续出现请查看插件日志 |
密码登录返回 429 TOO_MANY_ATTEMPTS | 该用户名或来源 IP 触发限流 | 等待 15 分钟窗口自然结束;确认不是有人在爆破 |
带 token 请求返回 401 UNAUTHENTICATED | ① 令牌过期 ② 令牌被吊销 ③ Header 格式错 | 检查 Bearer 后是否有空格;重新登录 |
登录后 permissions 为空数组 | 裁剪模式下用户无对应角色 | 在默认角色中补上 authenticated(见第 2 步) |
getConfigs 里没有 loginConfig | 设置页从未保存过该分组 | 进设置页保存一次 |
| 微信登录提示「密钥资源不存在」 | Secret 被删除但设置项还留着名字 | 重新选择/创建密钥 |
查看日志
插件日志中会记录微信 code2Session 的 errcode(不记录响应体,避免泄露敏感信息)。日志级别 WARN 及以上的认证失败都会留痕。
八、常见问题
Q:令牌和 Halo 后台手动创建的 PAT 有什么区别?
A:没有本质区别,都能访问 Halo API。唯一区别是插件签发的令牌会被插件定时清理(过期后自动移除),而手动创建的令牌一直保留、由你自己管理。
Q:可以限制某个用户不能登录移动端吗?
A:可以。在 Halo 后台把该用户禁用,密码登录与微信登录都会返回 USER_DISABLED。两种登录方式对禁用状态的处理是一致的。
Q:AppSecret 会被写进日志吗?
A:不会。微信接口的响应体从不落日志,只记录 errcode。
Q:换域名 / 换服务器后需要重新配置吗?
A:需要更新微信公众平台的 request 合法域名。Halo 侧无需改动(密钥存在 Halo 里,与域名无关)。
Q:令牌能不能手动延长?
A:不能。到期后必须重新登录。这是刻意设计 —— 无状态的短生命周期令牌比可续期的更安全。
Q:移动端能改昵称头像吗?
A:当前版本不能。 微信自动注册的用户停留在默认昵称(微信用户01 这种带序号的形式)与无头像状态。这是已知缺口,见下方路线图。
补充:
UserReconciler会在用户没有头像附件时强制清空spec.avatar,所以无法通过预设一个默认头像 URL 来解决,只能由客户端本地占位。
九、路线图
| 优先级 | 项 | 说明 |
|---|---|---|
| P1 | 资料更新接口 | PUT /auth/profile(昵称/简介)+ POST /auth/avatar(头像上传,落 Halo 附件系统) |
| P2 | 令牌续期 | 临近过期时无感刷新 |
| P2 | 登录审计日志 | 记录登录 IP、设备、时间 |
已完成:
- 禁用用户对微信登录生效(此前禁用状态只拦密码登录,是安全漏洞)
- 登录失败限流(用户名 5 次 / IP 20 次,15 分钟窗口,返回 429)
/auth/profile改为只读,不再重复签发令牌
十、微信绑定管理
插件通过 Halo 的 user:detail:tabs:create 扩展点,在原生用户详情页加了一个「微信绑定」选项卡:
- 查看该用户是否已绑定微信、绑定的微信标识(openid / unionid)、绑定时间
- 一键解绑(只删绑定关系,不删 Halo 用户,用户可重新绑定或改用密码登录)
Halo 原生只提供「用户中心 → 账号绑定」这种自己解绑自己的入口,Console 侧没有全量绑定关系的管理界面,这个选项卡补的正是管理员视角。
注意:Halo 的用户列表没有提供显示字段扩展点(
user:list-item:field:create不存在),所以无法在列表里加一列「是否微信用户」的标记。需要批量筛选时,可在列表搜索框输入用户名前缀(如unihalo)筛出自动注册的微信用户。
十一、相关文档
- 移动端登录 · 接口文档 —— App 端对接


