使用Cookie的Fetch API

341

我正在尝试使用新的Fetch API,但在处理Cookies时遇到了问题。具体来说,在成功登录后,未来请求中会有一个Cookie头部,但Fetch似乎忽略了该头部,因此我用Fetch发出的所有请求都被拒绝。

这是因为Fetch还没有准备好,还是Fetch不能与Cookies一起使用?

我用Webpack构建我的应用程序。我也在React Native中使用Fetch,但没有同样的问题。

10个回答

386

默认情况下,Fetch 不使用 cookie。要启用 cookie,请按照此方法进行操作

fetch(url, {
  credentials: "same-origin"
}).then(...).catch(...);

94
同源策略不再生效,使用 include 即可(请参见@Jerry的回答):https://developers.google.com/web/updates/2015/03/introduction-to-fetch - jpic
9
“include”只适用于跨域请求,而不适用于同源请求。官方文档:https://github.com/github/fetch#sending-cookies - HoldOffHunger
6
我相信 same-origin(仍然有效)意味着更多的标头将得到尊重(如cookies等),但您的代码无法完全访问响应内容。 - Coderer
2
@MartinBajcar 在fetch中的参数“credentials”不会读取任何cookie,它只是一个门,如果打开了(credentials:'include'),则允许传递cookie,如果没有打开,则不允许。 - John Balvin Arias
6
谢谢,根据我后来的理解,拥有HttpOnly Cookie意味着它不能被document.cookie读取,但仍然可以在ajax或fetch请求中使用。 - Martin Bajcar
显示剩余7条评论

304

除了@Khanetor的回答外,对于那些处理跨域请求的人: credentials: 'include'

示例JSON获取请求:

fetch(url, {
  method: 'GET',
  credentials: 'include'
})
  .then((response) => response.json())
  .then((json) => {
    console.log('Gotcha');
  }).catch((err) => {
    console.log(err);
});

https://developer.mozilla.org/zh-CN/docs/Web/API/Request/credentials


24
你如何设置cookie呢? - pomo
2
Cookie是从服务器端设置的。在我的情况下,我使用的是httponly cookie。 - Khanetor
4
@Khanetor 我可以使用 JavaScript 的 document.cookie 设置 cookie,然后发送请求吗? - ospider
3
我发现只需在 document.cookie 中设置值即可将其包含在请求中。 - skwidbreth
1
如果您正在使用Rails API并且不断遇到Access-Control-Allow-Credentials的CORS问题,请确保在Rack :: Cors配置中将资源上的credentials:true设置。 - Mathyou
显示剩余6条评论

117

我刚刚解决了。只用了两天的暴力破解。

对我来说,秘诀在于以下几点:

  1. 我调用了 POST /api/auth 并查看到 cookies 已成功接收。

  2. 然后调用 GET /api/users/ 并使用 credentials: 'include',但由于没有发送任何 cookies,因此得到 401 未经授权的状态码。

关键是设置第一个 /api/auth 调用的 credentials: 'include'


1
我有和你一模一样的问题。会话cookie在GET数据请求时从未被发送,因此返回401错误。我已经尝试了Axios和Fetch,但结果相同。有两种可能性:登录POST没有存储接收到的cookie,或者后续的GET数据没有发送存储的cookie。 - Rhubarb65
1
好的,谢谢。你的回答意味着我只用了一天的暴力破解。 :) - Michael
5
我欠你一大杯啤酒。我已经连续三个小时撞头解决这个问题了。非常感谢你,谢谢你一千遍。 - Gligor Atanasovski
3
另外,不要像我一样。确保获取的URL是127.0.0.1而不是localhost,否则Cookie将无法设置。 - Daniel Michaels
1
太棒了!我从没想过要尝试那样做。为什么需要在第一次请求中尝试发送一个cookie? - HetMes
显示剩余5条评论

35

如果您在2019年阅读此内容,credentials: "same-origin"是默认值。

fetch(url).then

5
请注意,并非所有人都在使用已经更新到足够版本的浏览器。我发现这个问题是因为我的Firefox版本(60.x,在Debian Stretch中是最新的)默认没有设置该功能。 - philh

20

在浏览器端以编程方式覆盖Cookie头文件不起作用

fetch文档中,提到了某些名称是被禁止的。而Cookie恰好是其中一个被禁止的标头名称,无法通过编程方式修改。例如,请看以下代码:

  • 如果在页面https://httpbin.org/的Chrome DevTools控制台中执行Cookie:'xxx=yyy',则会被忽略,并且如果存在cookie,则浏览器将始终将document.cookie的值作为cookie发送。
  • 如果在不同的起源上执行,则不会发送cookie。
fetch('https://httpbin.org/cookies', {
  headers: {
    Cookie: 'xxx=yyy'
  }
}).then(response => response.json())
.then(data => console.log(JSON.stringify(data, null, 2)));

顺便说一句,你可以通过在Chrome浏览器中打开https://httpbin.org/cookies/set/foo/bar来创建一个示例cookie foo=bar

有关详细信息,请参见禁止使用的标头名称


4

以下是针对 .net webapi2 用户的完整解答。

如果您正在使用 cors,因为您的客户端网站与您的 webapi 的地址不同,则还需要在服务器端配置中包含 SupportsCredentials=true

        // Access-Control-Allow-Origin
        // https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api
        var cors = new EnableCorsAttribute(Settings.CORSSites,"*", "*");
        cors.SupportsCredentials = true;
        config.EnableCors(cors);

'cors.SupportsCredentials = true;' 今天救了我的一天,谢谢。 - Fariborz Korevli
完全与问题无关。这是关于浏览器API和React Native的,而不是其他平台。 - undefined
@OlegMihailik 这只是为了指出如果没有正确设置CORS,会导致类似的错误。我的回答与React Native无关,尽管我怀疑它在那里也可能出现。 - undefined

2

如果在修复凭据后仍无法正常工作,请尝试以下方法。

我也曾使用过:

  credentials: "same-origin"

它曾经可以工作,然后突然就不行了,经过深入挖掘,我意识到我已经将我的网站URL更改为http://192.168.1.100以在局域网中进行测试,并且这是用于发送请求的URL,即使我在http://localhost:3000上。

因此,总之,请确保页面的域名与获取URL的域名匹配。


2
有任何想法是否与端口有关?当主机相同但端口不同时,这是如何工作的呢?(例如,UI 服务器 + 后端服务器) - Ryan
你好 @Ryan,实际上看起来端口与跨域资源共享(CORS)没有关系,但它们确实有关联:https://dev59.com/hGIj5IYBdhLWcg3ws3Nf#19966782 - Steve Moretz
请注意,即使是localhost也不等同于127.0.0.1 - 您的后端需要与前端完全相同的IP地址。 - hansaplast

2
这对我来说有效:
import Cookies from 'universal-cookie';
const cookies = new Cookies();

function headers(set_cookie=false) {
  let headers = {
    'Accept':       'application/json',
    'Content-Type': 'application/json',
    'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
};
if (set_cookie) {
    headers['Authorization'] = "Bearer " + cookies.get('remember_user_token');
}
return headers;
}

然后构建您的调用:
export function fetchTests(user_id) {
  return function (dispatch) {
   let data = {
    method:      'POST',
    credentials: 'same-origin',
    mode:        'same-origin',
    body:        JSON.stringify({
                     user_id: user_id
                }),
    headers:     headers(true)
   };
   return fetch('/api/v1/tests/listing/', data)
      .then(response => response.json())
      .then(json => dispatch(receiveTests(json)));
    };
  }

1
谢谢!这正是我一直在努力解决的问题!! 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content') 真是救了我一命! - Andrew

1
我的问题是我的cookie被设置在特定的URL路径上(例如,/auth),但我正在不同的路径上进行fetch。我需要将我的cookie的path设置为/

0
如果即使请求不打算进行跨域调用,但fetch没有发送凭据,可能是因为请求由于请求的源和响应位置之间的协议差异而被处理为跨域。
我观察到我的服务器返回了一个带有http URL的Location头部,而连接是通过https建立的。结果,浏览器将请求视为跨域并应用跨域规则。值得注意的是,Firefox(版本114.0.2)和Safari(版本16.1)在这种情况下没有显示任何警告,但Chrome(版本114.0.5735.198)显示了一个(blocked:mixed-content)错误,这有助于识别问题。
如果有人感兴趣,在这个特定的案例中,SSL终止是在反向代理中执行的,但由于错误配置,gunicorn服务器没有正确处理它,具体涉及到secure-scheme-headersforwarded_allow_ips设置。在解决了服务器端的这些设置后,fetch在所有浏览器中都正常工作了。

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