移动 WebView 使用 file:// 本地加载的 CORS cookie 凭据

31
请耐心等待,这个问题需要一些解释。
我正在帮助构建一个混合移动 Web 应用程序。主要代码库是 HTML5 和 JavaScript,将被包装在一个本地移动 Web 视图中 (类似于 Phonegap)。
应用程序的一部分功能需要将信息发布到由我们客户控制的 Web 服务中。由于该服务正在被其他人使用,因此几乎没有改变它的余地。我们使用 HTTP POST 发送 JSON 并从服务器接收响应。响应的一部分是 JSESSIONID cookie,它管理我们与服务器的会话。在初始的 initSession() 调用之后,我们需要在每个 (AJAX) 请求中发送 JSESSIONID cookie。
在移动设备上部署时,Web 应用程序被包装在本地 Web 视图中,通过浏览 file:///path/to/app/index.html 来启动 Web 应用程序。
我们尝试的第一件事是请求客户在其响应头中设置 Access-Control-Allow-Origin: *,以允许跨域资源共享 (CORS)。然后,我们尝试向服务器发送 POST 请求:
$.ajax({
  url: 'http://thirdparty.com/ws',
  data: data,
  type: "POST",
  dataType: "JSON",
  success: successCallback,
  error: failedCallback
});

从监控请求中可以发现,cookies没有被包含。仔细检查发现CORS规范有一个专门处理用户凭证的部分,其中包括会话cookie。因此,我修改了AJAX调用以包含它:

$.ajax({
  url: 'http://thirdparty.com/ws',
  data: data,
  type: "POST",
  dataType: "JSON",
  success: successCallback,
  error: failedCallback,
  xhrFields { withCredentials: true }
});

浏览器出现了另一个错误。更多的阅读揭示了以下信息:

如果第三方服务器没有响应 Access-Control-Allow-Credentials: true 标头,则响应将被忽略,无法提供给 Web 内容。

重要提示:在响应信任请求时,服务器必须在 Access-Control-Allow-Origin 标头中指定一个域,并且不能使用通配符。

因此,我们需要更改服务器的标头,包括将 Access-Control-Allow-Credentials: trueAccess-Control-Allow-Origin 设置为我们的 Origin。

这里终于来到我的问题:当使用 file:// 协议加载网页时,从 Web View 发送的请求头 Origin 被设置为 null。因此,服务器无法解析它,无法将其设置在 Access-Control-Allow-Origin 中。但是,如果服务器无法将 Access-Control-Allow-Origin 设置为除 * 之外的其他值,则我们无法发送凭据,包括 cookie。

所以我陷入了困境。怎么办呢?我在这里看到了一个类似的问题,但我不太理解提出的答案。任何帮助将不胜感激!


3
我能想到的唯一解决方案就是使用服务器来访问网络服务。你可以编写一个HTTP处理程序,并从移动应用中访问它。显然,这会增加服务器维护的额外开销,但你必须有可用的东西,对吧? - Alex Morales
是的,那是临时解决方案,一个不太理想的 PHP curl 中间网关。虽然并不完美,但至少目前能够工作。 - Tom Spencer
我可能会做的一件事是向PhoneGap开发人员询问他们是否之前遇到过类似的情况,并且是否有任何解决方法... - Tom Spencer
我们解决这个问题的方法是在服务器端使用nginx作为反向代理,将请求重定向到第三方认证系统并获取响应。如果您使用的是apache,您也可以采用同样的方法。或者您可以使用任何其他绕过同源策略的方法。具体方法请参考:https://dev59.com/F3A75IYBdhLWcg3w1s32。 - Pradeep Mahdevu
@AlexMorales 我不明白为什么使用这个服务器会解决 file:// 的问题,你能详细解释一下这个过程吗?谢谢。 - savram
6个回答

7
我知道这个问题很旧了,但我想无论如何都要发表一下意见。在跨源请求中,浏览器会进行预检。这意味着 - 尽管你使用任何的$.ajax()方法,但是一个OPTIONS请求仍将被发送到服务器。
实际上,这个预检测的OPTIONS请求是在说:
“嘿,来自其他域的外部服务器,我想发送一个非简单请求(简单请求不需要预检)。我的非简单请求将带有这些头和内容类型等等。你能让我知道这样做是否可以吗?”
然后服务器会执行它该执行的操作(可能会检查一些配置或数据库),并响应允许的来源、允许的标头和/或允许的方法。
最后,如果那个预检 OPTIONS 请求收到了允许实际 $.ajax() 方法进行的响应,那么它就会开始。
CORS与JSONP不同。
尽管成功的withCredentials预检查需要响应携带一个Access-Control-Allow-Credentials头(如问题所述),但除此之外还需要包含预期请求的组成部分的Access-Control-Allow-OriginsAccess-Control-Allow-Methods值。
例如,如果您正在使用具有标题somevaluePOST请求从源http://foo-domain.comhttp://bar-domain.com进行CORS请求,则将发送预检查OPTIONS请求,以便实际的POST请求可以被发送到http://bar-domain.com。为此,OPTIONS请求必须收到一个响应,其中包含http://foo-domain.com*Access-Control-Allow-Origins值。响应还需要包含一个Access-Control-Allow-Methods值,其中包括POST。这也可能是*。最后,如果我们想允许我们的somevalue标头,则响应必须包含一个Access-Control-Allow-Headers值,其中包括我们的somevalue标头键或*
回到问题本身-如果你不能控制服务器,或者没有办法让服务器允许你的CORS请求,你可以始终使用JSONP或一些urlEncoded数据类型和/或进行不带自定义标头的简单请求。GETHEAD和完整的POST请求通常都是简单请求。

2
附注:在响应经过认证的请求时,服务器必须指定一个域,不能使用通配符(*)。 - T J

2

1
我的建议是在服务器端将ACCESS-CONTROL-ALLOW-ORIGIN设置为null
是的,这个问题困扰了我一段时间。
关于CORS规范null可以满足来自file://方案的CORS请求。
实际上,该规范的一个实用建议是将其设置为origin-list-or-null,它可以是由空格分隔的起源列表或仅为“null”(顺便说一下,“origin-list-or-null”的定义中字符串%x6E %x75 %x6C %x6C是十六进制编码的null)。

最后你可能会问,如果我们将ACCESS-CONTROL-ALLOW-ORIGIN设置为null,是否等同于*,因为每个来自file://的请求都是有效的(这意味着每个混合应用程序都可以访问您的端点,如果它知道您的URI)?

嗯,鉴于Access-Control-Allow-Credentials: true,我相信您在服务器上已经有一个完整的身份验证机制正在运行。它应该过滤掉那些没有正确身份验证的请求。

希望这可以帮到您。


0
例如,在 PHP 方面,您需要设置这个:
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Request-With, Set-Cookie, Cookie, Bearer');
header('Access-Control-Allow-Credentials: true');
// header('Cookie: PHPSESSID='.$_COOKIE['PHPSESSID']);

0

使用JsonP请求。JsonP请求使您能够进行跨域请求。这里是一个例子。


0

试着看看 www.5app.co.uk。完全避免使用XHR调用,并在数据连接不稳定时可靠地在移动设备上工作。然后网关与您的客户端接口。


谢谢提供的信息。理想情况下,我不希望在我们开发应用程序的方式上进行彻底的改变,但我会看看5app能为我们提供什么。 - Tom Spencer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接