原生 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 的 None cookie 在现代引擎上被丢弃。
  • 预检抛不透明的错。 排查一次 CORS 拒绝是瞎的——又回到 WebView 调试黑盒那个老问题。
  • 绝望的歪招。 有些团队去开 --disable-web-security,生产环境根本不可行,还是合规雷。

runtime 解法

宿主自有的 runtime 掌控凭据。宿主把会话放进一处存储,通过一条鉴权通道桥接给页面——在原生层正确附带 cookie、遵守 SameSite 与 Secure,并把跨源请求代理出去,让 CORS 与预检在能被看见的地方处理。一个身份,Web 与原生共用,每次请求都带鉴权。

维度WebView宿主自有 runtime
Cookie 罐独立于系统与原生一处共享存储
跨站 cookie被 SameSite 丢弃在原生层被尊重
带凭据 fetch不透明的 CORS 失败隧道化、可观测
跨子域 SSO崩可用
预检调试瞎眼的 TypeError宿主记录日志
默认安全易配错强制

原生 App 从不会在网络边界丢失会话。裸 WebView 却经常如此。runtime 带着一个身份翻过这道墙——并为每次请求做鉴权。

参考来源