在某些 Android 设备中,Cordova 不会根据请求发送来源

Cordova doesn't send origin on request in some Android devices

这是一些 android 设备上会发生的问题。

我有一个 Samsung Galaxy A5 (2017) Google Chrome version 76.0.3809.89 和 Android 版本 8.0.0。

当我第一次在此设备上部署我的 Cordova 应用程序并发出 POST 请求时,我收到有关 CORS 的错误消息:

Access to XMLHttpRequest at 'http://foo.com/bar' from origin 'file://' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

我可以从 Chrome 的开发工具网络选项卡中看到这个 POST 请求没有 origin 参数。

如果我从任务管理器关闭应用程序并再次启动它,我可以成功发出此请求并且 chrome 根据请求发送 'origin' 参数。

如果我从设备的设置中删除应用程序的数据,我可以再次重现该问题。

我完全确定这不是 Content-Security-Policycordova-whitelist-plugin 配置的问题,因为正如我之前提到的,在其他一些 android 设备上应用程序工作正常。

我需要提及的另一件事是,使用人行横道插件时我没有遇到这个问题。

这是 Chromium webview 上的一个公开错误,有趣的是 76 版本;在此特定版本中,OOR-CORS 在第一个 运行.

中保持启用状态

https://bugs.chromium.org/p/chromium/issues/detail?id=991107#c14

变更列表将直接合并到76分支。

我们遇到了同样的问题。我们首先尝试实施一种解决方法,即应用程序检测到 Chrome 版本。第一次看到版本 76 时,它会自动重启应用程序。大多数用户甚至不会注意到重启。解决方法基于几份报告,其中指出,问题只会在安装 Chroe 76 后的第一个 运行 应用程序期间出现,并在重新启动后永久消失。然而,事实证明,这个问题经常 returns 似乎是随机的,因此解决方法还不够。

幸运的是我们也控制了服务器端,所以我们在那里实施了一个解决方法。基本上我们只接受 file:// 原点。此解决方法的安全影响当然取决于各个应用程序,因此请仔细考虑,以防您考虑实施类似的方法。

在我们的例子中,我们使用 ASP.NET,我们面临着额外的挑战。不幸的是,将 file:// 指定为源时 EnableCorsAttribute 失败,这符合 CORS 规范,该规范要求每个源中都存在主机名(指定 * 时除外)。所以我们最终想出了以下解决方案(当然是针对ASP.NET):

创建一个 ICorsPolicyProvider,在原始 file:// 的情况下覆盖验证逻辑,并在所有其他情况下委托给嵌套的提供者(原始提供者):

    /// <summary>
    /// Object that allows clients with `file://` origin. This is used as a workaround
    /// for issues on Android devices running Chrome 76. See
    /// https://bugs.chromium.org/p/chromium/issues/detail?id=991107#c14
    /// 
    /// Simply adding `file://` to the allowed origins list doesn't work, because by
    /// specification a hostname is required.
    /// 
    /// This workaround should be removed as soon as Chrome 76 distribution has dropped
    /// sufficiently.
    /// </summary>
    // TODO: Remove this workaround once Chrome 76 is not around anymore.
    public class CorsPolicyProviderAcceptingFileOrigin : ICorsPolicyProvider
    {
        public readonly ICorsPolicyProvider inner;

        public CorsPolicyProviderAcceptingFileOrigin(ICorsPolicyProvider inner)
        {
            this.inner = inner;
        }

        public async Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (
                request.Headers.TryGetValues("origin", out IEnumerable<string> origins)
                && origins.SequenceEqual(new[] { "file://" })
                )
            {
                var policy = new CorsPolicy()
                {
                    AllowAnyHeader = true,
                    AllowAnyMethod = true,
                    AllowAnyOrigin = false,
                };
                policy.Origins.Add("file://");

                return policy;
            }

            return await this.inner.GetCorsPolicyAsync(request, cancellationToken);
        }
    }

然后在WebApiConfig.cs中这样使用它:

// get the original EnableCorsAttribute
ICorsPolicyProvider cors = ...;

// this is a workaround for Android devices running Chrome 76 and should be removed in future versions.
// See https://bugs.chromium.org/p/chromium/issues/detail?id=991107
cors = new CorsPolicyProviderAcceptingFileOrigin(cors);

config.EnableCors(cors);

我们遇到了同样的问题...AJAX 在我们的 Cordova 项目中第一次冷启动后调用将无法工作。如果我们重新启动应用程序(从后台删除应用程序)那么它就可以工作......这似乎是 Chromium WebView 版本的问题......我发现了一个非常难看的解决方法。使用

https://github.com/dpa99c/cordova-diagnostic-plugin#restart

插件并在第一次冷启动后重新启动应用程序。在 localStorage 中检查冷启动标志。如果找不到,请重新启动应用程序。