You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS Safari与React Native WebView中Django持久化HttpOnly Cookie失效问题排查

iOS Safari与React Native WebView中Django持久化HttpOnly Cookie失效问题排查

我之前踩过几乎一模一样的iOS生态Cookie持久化坑,结合你的代码和场景,咱们一步步拆解问题根源和落地的解决办法:

核心原因:iOS生态的隐私机制限制

你的Cookie配置在安卓和iOS Chrome正常,但iOS Safari/WebView失效,90%以上是**苹果的智能跟踪预防(ITP)**在起作用,再加上WKWebView的特殊Cookie存储逻辑,具体来说:

  1. ITP对跨站/通配符Cookie的严格限制:iOS 14.5+的ITP会把带SameSite=None+通配符域名(.myweb.net)的Cookie判定为"跟踪型Cookie",即使你设置了max_age,也会强制降级为会话Cookie,关闭浏览器/WebView就自动删除。
  2. WKWebView的Cookie共享逻辑:React Native的WebView基于WKWebView,默认情况下Cookie存储和系统Safari是隔离的,即使你开了sharedCookiesEnabled,也可能因为ITP的限制导致Cookie无法持久化到系统存储。

分步解决方案

一、Django后端调整(优先改这里,最有效)

你的COMMON配置逻辑没问题,但针对iOS ITP做以下优化:

1. 调整Domain配置(关键!)

通配符域名是ITP重点打击的对象,优先做以下尝试:

  • 如果前端和后端同域:直接删除domain参数,让Django自动使用当前请求的域名,ITP对同域Cookie的限制宽松很多:
MAX_AGE = 60 * 60 * 24 * 360
COMMON = {
    "httponly": True,
    "secure": True,
    "samesite": "None",
    "path": "/",
    # 注释掉通配符domain,让Django自动适配当前域名
    # "domain": ".myweb.net",
    "max_age": MAX_AGE,
}
  • 如果必须用子域共享:把domain改成WebView实际加载的具体子域,比如WebView加载的是app.myweb.net,就把domain设为"app.myweb.net",避免通配符。

2. 验证Set-Cookie响应头

在Safari开发者工具(iOS设置→Safari→高级→开启开发者模式,然后Mac上用Safari连设备调试)里,查看后端返回的Set-Cookie头,确保:

  • 同时存在SecureSameSite=None(iOS 14.5+强制要求,缺一不可)
  • Expires字段是未来的有效日期(Django会自动把max_age转成Expires,如果显示为Session说明配置没生效)
  • 没有被其他中间件篡改Cookie属性(比如有没有中间件偷偷加了SameSite=Lax

3. 避免不必要的通配符

如果你的前端和后端都是myweb.net下的子域,完全不需要通配符域名,直接用当前请求的域名更安全,也能绕过ITP的跟踪判定。

二、React Native WebView端优化

你的WebView配置已经加了关键参数,但针对WKWebView补全以下配置:

1. 升级react-native-webview到最新稳定版

旧版本(<13.5.0)的sharedCookiesEnabled在iOS上有严重bug,无法正确共享系统Cookie存储,建议升级到13.6.3+:

yarn add react-native-webview@latest
# 或
npm install react-native-webview@latest

2. 补全WKWebView的存储权限配置

Info.plist里添加以下配置,确保WebView有足够的存储权限:

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
    <!-- 如果你的域名是HTTPS,也可以加NSExceptionDomains更安全 -->
</dict>
<key>NSUserTrackingUsageDescription</key>
<string>需要使用Cookie来保持登录状态</string>

(注:NSUserTrackingUsageDescription是iOS 14+要求的,即使你不跟踪用户,也需要说明Cookie的用途,避免被系统拦截)

3. 调整WebView加载逻辑

先触发后端设置Cookie的接口,再加载WebView,确保Cookie已经写入系统存储:

// 伪代码
const loadWebView = async () => {
  // 先请求后端的登录/设置Cookie接口,确保Cookie写入系统存储
  await fetch('https://api.myweb.net/auth/set-cookie', { credentials: 'include' });
  // 再加载WebView
  webRef.current?.reload();
};

// 在组件挂载后调用
useEffect(() => {
  loadWebView();
}, []);

4. 验证WebView的Cookie状态

react-native-cookies库检查系统存储里的Cookie是否存在:

yarn add react-native-cookies
import CookieManager from '@react-native-cookies/cookies';

// 在WebView加载后检查
const checkCookies = async () => {
  const cookies = await CookieManager.get('https://myweb.net');
  console.log('系统存储的Cookie:', cookies);
  // 看refresh_token/access_token的expires字段是否为未来时间
};

三、Safari端验证与排查

  1. 关闭私有浏览模式:iOS Safari的私有模式下,所有Cookie都会在关闭后删除,测试时确保是正常浏览模式。
  2. 检查ITP状态:在Safari→设置→隐私与安全性→查看"隐私报告",看看你的域名是否被标记为"跟踪器",如果是,ITP会强制限制Cookie。
  3. 关闭"阻止跨站跟踪":测试时临时关闭这个选项,如果Cookie能持久化,就坐实是ITP的问题,回到后端调整Domain配置。

最终验证步骤

  1. 调整Django的Domain配置后,用Safari正常模式打开你的网站,登录后关闭浏览器,重新打开,检查Cookie是否存在。
  2. 在React Native WebView里,登录后杀死APP,重新打开,检查WebView是否保持登录状态。
  3. 用Safari开发者工具查看Cookie的Expires字段,确认是未来的有效日期。

如果还是有问题,大概率是你的iOS版本过旧(比如<14.0,不支持SameSite=None),但你提到iOS Chrome正常,所以版本应该在14.5+,可以排除这个情况。

火山引擎 最新活动