React Native - Webview 设置 cookies 策略

React Native - Webview set cookies policy

为了给您提供上下文,我们的应用程序是一个 react-native 网络包装器(类似于 Cordova 或 ionic 所做的)。

应用程序会在用户打开应用程序后下载本地存储的更新版本的网络包,然后 WebView 组件将其用作向用户显示应用程序的源。

这个网络应用程序的某些部分使用网络套接字,所以问题是考虑到它的大流量,socket/exchange 服务有一堆 nodes/replicas 并且为了保持连接alive 负载均衡器使用 Set-Cookie headers 来确保客户端在每个请求中都转到正确的服务节点

所以,我们正在尝试的是:

我们的 react-native 应用程序看起来像:

import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {WebView} from 'react-native-webview';

type WrapperProps = {
  path: string;
};

function Wrapper({path}: WrapperProps) {
  return (
    <View style={styles.container}>
      <WebView
        allowFileAccess
        allowUniversalAccessFromFileURLs
        allowingReadAccessToURL={path}
        javaScriptEnabled
        originWhitelist={['*']}
        sharedCookiesEnabled
        source={{
          uri:
            Platform.OS === 'ios'
              ? `file://${path}/index.html`
              : `${path}/index.html`,
        }}
        injectedJavaScript={`(function () {
          window.ReactNativeWebView.postMessage(JSON.stringify(document.coookie));
          var open = XMLHttpRequest.prototype.open;
          XMLHttpRequest.prototype.open = function (method, url) {
            this.addEventListener("load", function () {
              var responseHeaders = this.getAllResponseHeaders();
              var setCookies = this.getResponseHeader("Set-Cookie"); // always return null
              window.ReactNativeWebView.postMessage(JSON.stringify({ setCookies }));
              window.ReactNativeWebView.postMessage(
                JSON.stringify({ responseHeaders }) // return all headers except for Set-Cookie 
              );
            });
            open.apply(this, arguments);
          };
        })();
        `}
        onMessage={e => {
          console.log({onMessageEvent: e.nativeEvent.data});
        }}
        style={styles.webview}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  webview: {
    height: '100%',
    width: '100%',
  },
});

export default React.memo(Wrapper);

你知道如何解决这个问题吗?

P.S:我们已经阅读了 How to set WKWebView cookie to accept Policy - 但它适用于 swift。

我设法

Enable Set-Cookie behavior within a WebView

通过使用 socket.io-client 建立套接字连接,因此 WebView 中的网络套接字请求将已经在请求中包含 cookie headers(这将保持连接处于活动状态)。

考虑 react-native-webview 在 iOS、there is no way to access cookies in response headers 上使用 WKWebView。但是,在这种特定情况下,您可以从 Web 视图外部创建套接字连接,处理 cookie(似乎 socket.io-client 已经这样做),然后它们应该在 WebView 内的请求中可用。

为了在您的 react-native 应用程序中设置套接字 io,您需要

  • 安装 socket.io-client@2.1.1(我尝试了不同的版本,但这是唯一有效的版本)
  • 赋值window.navigator.userAgent = 'react-native';(由于ES6模块被提升,这个赋值不能在与react-native和socket io imports相同的文件中完成)
  • 建立套接字连接。

所以它应该看起来像:

// userAgent.js
window.navigator.userAgent = 'react-native';

// Wrapper.tsx
import React from 'react';
import {Platform, StyleSheet, View} from 'react-native';
import {WebView} from 'react-native-webview';
// user agent MUST be imported before socket io
import './userAgent';
import io from 'socket.io-client';


export const socket = io('https://your.host.com/', {
  path: '/path/to/socket.io/',
  jsonp: false,
});


type WrapperProps = {
  path: string;
};

function Wrapper({path}: WrapperProps) {
  return (
    <View style={styles.container}>
      <WebView
        allowFileAccess
        allowUniversalAccessFromFileURLs
        allowingReadAccessToURL={path}
        javaScriptEnabled
        originWhitelist={['*']}
        sharedCookiesEnabled
        source={{
          uri:
            Platform.OS === 'ios'
              ? `file://${path}/index.html`
              : `${path}/index.html`,
        }}
        style={styles.webview}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  webview: {
    height: '100%',
    width: '100%',
  },
});

export default React.memo(Wrapper);