为啥即使对于同一个站点 Ajax 请求也需要 XMLHttpRequest.withCredentials

Posted

技术标签:

【中文标题】为啥即使对于同一个站点 Ajax 请求也需要 XMLHttpRequest.withCredentials【英文标题】:why is XMLHttpRequest.withCredentials necessary even for same site Ajax requests为什么即使对于同一个站点 Ajax 请求也需要 XMLHttpRequest.withCredentials 【发布时间】:2017-04-05 03:38:45 【问题描述】:

我正在尝试实现一个身份验证服务,该服务部署在与我的登录页面不同的 HTTP 服务器中。

下图描述了我的设置:

在第 1 步,我的浏览器发出 HTTP GET 请求以获取登录页面。这在 #2 中作为 HTTP 响应提供。我的浏览器呈现登录页面,当我单击登录按钮时,我将HTTP POST 发送到不同的服务器(在同一个本地主机上)。这是在#3 中完成的。身份验证服务器检查登录详细信息并发送响应以设置 #4 中的 cookie。

#3 中的 ajax POST 请求是使用 jQuery 发出的:

$.post('http://127.0.0.1:8080/auth-server/some/path/',
       username: 'foo', password: 'foo',
       someCallback);

身份验证服务的响应(假设身份验证成功)具有以下标头:

HTTP/1.1 200
Set-Cookie: session-id=v3876gc9jf22krlun57j6ellaq;Version=1
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: origin, content-type, accept, authorization
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 21 Nov 2016 16:17:08 GMT

因此 cookie (session-id) 出现在第 4 步的 HTTP 响应中。

现在,如果用户再次尝试登录,我希望身份验证服务能够检测到这一点。为了测试这种情况,我再次按下登录按钮以重复 #3 中的帖子。我本来希望第二个请求包含 cookie。但是我第二次按下登录按钮时,#3 发出的帖子中的请求标头不包含 cookie。

我发现要让#3 中的帖子包含 cookie,我必须这样做:

        $.ajax(
            type: 'post',
            url: 'http://127.0.0.1:8080/auth-server/some/path',
            crossDomain: true,
            dataType: 'text',
            xhrFields: 
                withCredentials: true
            ,
            data: 
                username : 'foo',
                password : 'foo',
            ,
            success: someCallback
        );

为什么有必要这样做? MDN 声明这仅对跨站点请求是必需的。 This SO post 也使用xhrFields,但仅与跨域场景有关。我知道我的情况不是跨域的,因为提供脚本的页面都在本地主机上,而发送 ajax 请求的页面在同一主机上。我也知道 cookie 域是not port specific。此外,由于我的 cookie 没有明确指定域,所以 the effective domain is that of the request 表示 127.0.0.1 与我第二次发送 POST 请求时相同(#3)。最后,#4 上的 HTTP 响应已经包含 Access-Control-Allow-Origin: *,这意味着资源 can be accessed by any domain in a cross-site manner。

那么为什么我必须使用xhrFields: withCredentials: true 来完成这项工作?

我的理解是设置Access-Control-Allow-Origin: * 只是启用跨站点请求,但是为了发送cookie,无论如何都应该使用xhrFields: withCredentials: true(如MDN section on requests with credentials 中所述)。此外,我知道请求确实是跨站点的,因为端口号在决定请求是否跨站点时很重要。 cookie 的域是否包含端口是无关紧要的。这种理解正确吗?

更新

我觉得this answer已经解释的很清楚了,所以也许这个问题应该删掉。

【问题讨论】:

是的,但是他们使用不同的端口...这已经被认为是 CORS 【参考方案1】:

源的所有部分都必须与主机(ajax 目标)匹配,才能被视为同源。来源https://sales.company.com:9443的3部分例如包括:

    协议/方案(https - 与 http 不匹配) 主机名(sales.company.com - 与 subdomain.sales.company.com 不匹配) 端口(9443 - 不匹配 443)

见https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy

【讨论】:

以上是关于为啥即使对于同一个站点 Ajax 请求也需要 XMLHttpRequest.withCredentials的主要内容,如果未能解决你的问题,请参考以下文章

为啥提交ajax请求时请求随机为空?

为啥我的 PDF 生成进程会锁定我站点中的其他 ajax 进程?

如何在页面离开时终止长轮询 Ajax 请求

即使正确设置了 document.domain,跨子域 ajax 请求也会被拒绝

仅使用 websockets 构建整个站点(通过 socket.io 和 node.js,没有 Ajax)?

为啥即使未启用 CORS,HTTP 请求也会在 Action 中处理? [复制]