科尔多瓦在某些Android设备上未根据请求发送来源

yan*_*iou 10 android android-webview cors cordova cordova-plugins

这是某些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.
Run Code Online (Sandbox Code Playgroud)

我可以从Chrome的开发工具网络标签中看到,此POST请求没有该origin参数。

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

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

我完全相信,这不是一个问题Content-Security-Policycordova-whitelist-plugin因为正如我之前提到,在其他一些Android设备应用程序的工作就好了配置。

我需要提及的其他事情是,使用人行横道插件时我没有这个问题。

sys*_*out 7

这是Chromium Webview上的一个开放式错误,有趣的是76版本;在此特定版本中,OOR-CORS在首次运行时保持启用状态。

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

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


Mar*_*usM 3

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

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

在我们的例子中,我们使用 ASP.NET,但我们面临着额外的挑战。不幸的是,EnableCorsAttribute指定file://为源时会失败,这符合 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它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);
Run Code Online (Sandbox Code Playgroud)