原生 App 与自己的网络栈共享一套凭据存储。进了 WebView,这套存储被隔开:cookie 罐独立于系统浏览器、也独立于原生 HTTP;跨站 cookie 被 SameSite 规则丢弃;每次带凭据的 fetch 都要过一道页面无法完全掌控的 CORS 握手。宿主自有的 runtime 提供凭据隧道——一条横跨 Web 与原生的鉴权通道。
WebView 自留一个 cookie 罐
在 Android 上,CookieManager 是「每个 WebView 配置档一份」,绝不是系统浏览器的那个罐。在 iOS 上,WKHTTPCookieStore 与 NSHTTPCookieStorage 相互独立;而且自 iOS 14 起,WebView 强制启用智能防追踪(ITP)——第三方 cookie 七天过期、跨站读取被拦。原生登录种下的会话 cookie 不会自动抵达页面里的 fetch(),于是请求回来就是 401。
SameSite 悄悄丢掉凭据
Chrome 80+ 把 SameSite=Lax 设为默认,并要求任何 SameSite=None 的 cookie 必须带 Secure;iOS 15.4+ 把 None 绑定到 Secure 与 Partitioned,否则丢弃。于是,一个缺了这些属性的 cookie 在跨站请求里干脆不被发送。叠加 CORS,带凭据的 fetch 需要 Access-Control-Allow-Credentials: true 和一个明确的 Access-Control-Allow-Origin(绝不能是 *)——而预检一旦失败,抛出的却是一个页面无法检查的 TypeError。
鉴权翻墙时会崩什么
- 登录成功,转头 401。 原生种了会话,WebView 的
fetch从不携带。 - 跨子域 SSO 失败。
login.example.com→app.example.com的 cookie 同步崩了,而 Safari 好好的。 - SameSite 丢掉令牌。 不带
Secure的Nonecookie 在现代引擎上被丢弃。 - 预检抛不透明的错。 排查一次 CORS 拒绝是瞎的——又回到 WebView 调试黑盒那个老问题。
- 绝望的歪招。 有些团队去开
--disable-web-security,生产环境根本不可行,还是合规雷。
runtime 解法
宿主自有的 runtime 掌控凭据。宿主把会话放进一处存储,通过一条鉴权通道桥接给页面——在原生层正确附带 cookie、遵守 SameSite 与 Secure,并把跨源请求代理出去,让 CORS 与预检在能被看见的地方处理。一个身份,Web 与原生共用,每次请求都带鉴权。
| 维度 | WebView | 宿主自有 runtime |
|---|---|---|
| Cookie 罐 | 独立于系统与原生 | 一处共享存储 |
| 跨站 cookie | 被 SameSite 丢弃 | 在原生层被尊重 |
| 带凭据 fetch | 不透明的 CORS 失败 | 隧道化、可观测 |
| 跨子域 SSO | 崩 | 可用 |
| 预检调试 | 瞎眼的 TypeError | 宿主记录日志 |
| 默认安全 | 易配错 | 强制 |
原生 App 从不会在网络边界丢失会话。裸 WebView 却经常如此。runtime 带着一个身份翻过这道墙——并为每次请求做鉴权。
参考来源
- MDN — CORS — 拦在跨源请求前的预检、凭据与
Access-Control-Allow-Origin规则。 - web.dev — SameSite Cookie 详解 — 为什么
Lax是默认、None必须带Secure。 - Android Developers — CookieManager — WebView 与系统浏览器分开保存的「每配置档」cookie 罐。
- Apple — WKHTTPCookieStore — 独立于
NSHTTPCookieStorage的 WebView cookie 存储。 - Chrome for Developers — SameSite 更新 — Chrome 80 把默认改成 Lax、从而弄坏无声跨站 cookie 的变更。