Captive Portal · 端到端时序研究

把"检测 → 重定向 → 认证 → 放行"
画成可演化的时序图

四个关键时序:核心认证流程、小米手机故障还原、Universal Link 修复后流程,以及漏配白名单时的回环失败。点击播放、逐步查看,每一步都有角色高亮与代码片段说明。

7 个参与方 1 个真实故障 1 套修复方案 交互式动画
Diagram 01

核心认证流程 · 端到端时序

从设备接入 Wi-Fi 到 OS 标记网络为 VALIDATED,分为三阶段:首次检测、浏览器认证、二次检测确认,共 32 步,并显式展示 walled garden 的方式 A / 方式 B 分支。OS 是这场戏的"裁判"——它不信任网关的任何声明,必须自己再探测一次。

DIAGRAM 01 · 三阶段 32 步

核心认证流程(端到端时序)

7 个参与方协作:客户端 / 系统 OS / 浏览器 / 网关 / 认证后端 / 互联网

关键洞察

OS 才是"裁判"

OS 不关心 Portal 是什么,只比对"我请求 generate_204 应该收到 204,结果却收到 302" → 标记 CAPTIVE_PORTAL。

探测 URL 同样被 302

反直觉:未认证时网关对探测 URL 也注入 302,这是 OS 唯一的 captive 信号。RFC 6585 还为此定义了 511 状态码。

二次检测:OS 不信任网关

即使 Portal 后端告诉网关"已放行",OS 仍必须自己再探测一次验证(independent verification)。

Diagram 02

小米手机故障还原 · 微信唤起卡死

Portal 页面正常打开,但用户点击"小程序认证"按钮后流程中断。根因:MIUI 沙箱 WebView 不能识别 weixin://,而 walled garden 又"帮了倒忙"。

DIAGRAM 02 · 故障还原

小米手机 Portal 唤起微信小程序失败

沙箱 WebView → Mi Browser → walled garden 三重失误

三个关键卡点

卡点 1 · 沙箱不认 weixin://

MIUI 沙箱 WebView 不能启动其他 App,不能处理 weixin:// 等自定义 URL Scheme,于是弹窗要求"在默认浏览器中打开"。

卡点 2 · 跳到 Mi Browser 后意图丢失

Mi Browser 启动后,原始的 weixin:// 链接已被丢弃。它转而做自己的 captive 检测 → 访问 connect.rom.miui.com

卡点 3 · Walled Garden 帮了倒忙

小米检测域必须在白名单(否则 Portal 页弹不出来),但 Mi Browser 也命中这条规则 → 200 OK → 判定"网络正常" → 停在小米空白页。

Diagram 03

修复方案 · Universal Link 唤起

把链接从 weixin:// 换成 https://servicewechat.com/...,并把微信域名加入 walled garden。MIUI 沙箱虽不能直接启动微信,但 HTTPS 链接会被系统层 App Link 拦截转给微信。

DIAGRAM 03 · 修复方案

使用 Universal Link 修复后的时序

HTTPS + Walled Garden + 系统级 App Link 调度

关键原理(修复为何能工作)

原理 1 · MIUI 沙箱能开 HTTPS

虽然不能启动其他 App,但 MIUI 沙箱 WebView 是个浏览器,能正常打开 HTTPS 链接——这在它能力范围内。

原理 2 · Universal Link 是系统级

Universal Link 不是浏览器功能,是 Android/iOS 系统级功能。微信安装时已注册:servicewechat.com 域的 HTTPS 链接 → 由我处理。系统会劫持请求转给微信。

原理 3 · Walled Garden 放行微信域

必须在网关配 servicewechat.comweixin.qq.com 等微信域名为白名单,否则请求会被重定向回 Portal 登录页,微信永远收不到。

Diagram 04

白名单漏配 · Universal Link 也会卡死

如果 servicewechat.com 没进 walled garden,HTTPS 链接会被网关 302 回 Portal,系统层还没来得及把请求交给微信。

DIAGRAM 04 · 白名单漏配

servicewechat.com 未放行时的回环失败

MIUI 沙箱 → Portal 网关 → 302 回 Portal,微信完全收不到请求

方案对比

weixin:// vs Universal Link · 效果差异

同样是"在 Portal 中拉起微信",旧方案在受限 WebView 上必然失败,新方案借助系统层调度绕过了这一限制。

❌ 旧方案 · URL Scheme

weixin://dl/business/...

仅微信能识别;MIUI 沙箱 WebView 无法处理
跳转 Mi Browser 后原始意图丢失
walled garden 的 captive 检测放行反而"帮倒忙"
用户卡死在小米的空白检测页
微信从未被唤起,流程中断
✅ 新方案 · Universal Link

https://servicewechat.com/wx{AppID}/...

普通 HTTPS 链接,MIUI 沙箱能正常发起
Android/iOS 系统层 App Link 拦截转交
walled garden 配置 servicewechat.com 即可
任何浏览器都能打开(未装微信时回退到网页)
已装微信时被微信拦截,直接拉起小程序

weixin:// 切到 servicewechat.com

这是"受限 WebView 无法拉起第三方 App"问题的标准范式。Portal 唤起支付宝小程序、银行 App、企业 SSO App 同样适用。

查看修复方案 →