跨站点cookie问题: 如何从后端设置cookie到前端

26

我正在开发我的第一个Web应用程序,前端使用React,后端使用FastAPI

我试图与Chrome一起测试它 - 查看前端是否正确地调用了后端的API,并显示结果。我一直在遇到有关cookies的问题,希望得到帮助。事先对这篇长文章道歉-过去几天我已经查阅了许多资源,现在我不确定哪些是相关的,哪些不是。

  • 前端位于localhost:8080
  • 后端位于http://127.0.0.1:8000
  • 以下为FastAPI后端代码中CORS的正确设置(我认为):
app = FastAPI()

origins = [
    "http://localhost:8080"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


情况: 前端向后端发出 GET 请求 http://127.0.0.1:8000/api/abc,后端设置了一个 cookie。

/*====================
尝试 1:
使用以下后端代码设置 cookie:

response.set_cookie(key="abcCookieKey", value="abcCookieValue")

使用以下前端JS代码进行GET请求:

fetch('http://127.0.0.1:8000/api/abc', {
            method: 'GET',
            credentials: 'include',
        })

第一次尝试的结果:
在Chrome的控制台选项卡中,我收到以下警告:

A cookie associated with a cross-site resource at http://127.0.0.1/ was set without the `SameSite` attribute. It has been blocked, as Chrome now only delivers cookies with cross-site requests if they are set with `SameSite=None` and `Secure`. You can review cookies in developer tools under Application>Storage>Cookies and see more details at https://www.chromestatus.com/feature/5088147346030592 and https://www.chromestatus.com/feature/5633521622188032.

在网络选项卡上,当我检查set-cookie响应头时,会得到以下消息:

This Set-Cookie was blocked because it has the "SameSite=Lax" attribute but came from a cross-site response which was not the response to a top-level navigation.

====================*/


...所以我进行了一些研究,并想出了

/*====================
尝试2:
使用以下后端代码设置cookie:

response.set_cookie(key="abcCookieKey", value="abcCookieValue", samesite="none", secure=True)

使用相同的前端JS代码进行GET请求。

第二次尝试的结果:
在Chrome的Console选项卡中,我收到了与第一次尝试相同的警告,即使响应标头具有set-cookieSamesite = none; Secure。此外,标头还具有以下警告

This Set-Cookie was blocked because it had the "Secure" attribute but was not received over a secure connection.

====================*/


..所以我尝试使用 https 并得到:

/*====================
尝试 3: 除了在我的 JS fetch 代码中,我使用 fetch('https://127.0.0.1:8000/api/abc ...,其他和尝试 #2 相同,
然后我在运行 uvicorn 的后端收到以下警告: WARNING: Invalid HTTP request received.

====================*/

问题:

  1. 我在尝试 #2 中有遗漏吗?肯定有一种简单的方法可以从后端向前端设置 cookie 而不必担心 https 吧?
  2. 如果我别无选择只能使用 https,如何本地运行可以通过 https 访问的后端服务器?我所做的研究似乎表明这是一个复杂 / 费时的过程。(但公平地说,我对 Web 开发 / 所有网络相关的事情的理解非常有限)。

简短的回答是不,我没有解决它。我最后用Safari绕过了它,然后在部署后检查了它在Chrome上是否工作。 - hainabaraka
1
你是如何解决它的,Shashan? - consoleart
1
请在@ShashanSooriyahetti处发布答案,正如您所看到的,有很多人想知道。 - Marisha
1
@Marisha 我已经添加了答案,如你们所请求的,请随意提问。 - Shashan Sooriyahetti
未来的读者可能会发现这些相关答案有帮助:这里这里,以及这里这里 - Chris
显示剩余3条评论
3个回答

11

我会尝试解释这个概念。

首先,请注意我正在使用由SailsJS开发的后端和由AngularJS开发的前端,以及连接到后端的应用程序,这些应用程序是使用Angular9开发的。

我在前端(CMS)中使用仅限于http的cookie来授予已登录用户添加内容的权限。在验证成功时,HTTP仅限制的cookie将设置给他们。

请注意,http cookie与后端和前端具有不同的URL,就像所提供的场景一样,这是一个棘手的部分。

要点:

  1. 因此,在开发期间,您必须在同一个IP下托管后端和前端。 例如,我的后端:192.168.8.160:1337,前端:192.168.8.160:81。 不同的端口相同的IP。

  2. 当进入生产时,这不是方案,您可以拥有任何域 :)

  3. 您必须在后端允许CORS,并接受前端IP:端口作为接受的来源。

  4. 实施,您必须确定您的环境,在我的情况下,


if (sails.config.environment === 'development'){
  res.setHeader('Set-Cookie',[`token=${token};  Path=/;HttpOnly; maxAge=86400000;SameSite=false;`]);
} else {
  res.setHeader('Set-Cookie',[`token=${token};  Path=/;HttpOnly; maxAge=86400000;SameSite=None;Secure=true;`]);
}


请注意,在开发环境中,您需要使用SameSite = false。因此,您需要具有相同的IP地址,因为我们无法使用SameSite = None,因为它需要使用Secure=true,而这将需要HTTPS。在开发模式下要实现这一点将会很麻烦。

就是这样,伙计们。如果你做对了,你就能实现它。


1
非常感谢您,这个答案节省了我很多时间。我使用Django + React(JWT Auth.)当我在后端设置Httponly cookie时,在前端API请求中没有设置。 - Pradip Kachhadiya
1
我已经尝试了整整一天来解决完全相同的问题。这个答案为我解决了问题。我仍然不明白samesite false和same samesite none之间的区别是什么。我以为它们是完全相同的东西。无论如何,现在这样做就可以了。 - Karim Kamel
@KarimKamel 很高兴能帮到你,你可以点个赞;)SameSite=none 的区别在于你可以将此 cookie 用于其他域,但需要使用 https,但由于在开发环境中无法实现,因此我们使用 SameSite=false,我们可以使用相同的 IP 和不同的端口而不是使用不同的 IP。因为它使用相同的 IP,所以您不需要 HTTPS。 - Shashan Sooriyahetti
1
如果您正在使用create-react-app并且想要使用HTTPS,只需在package.json中的启动脚本中添加HTTPS=true:scripts: { "start": "HTTPS=true react-scripts start" } - Rem
1
@Rem 不错,Angular也支持使用ng s --ssl进行HTTPS。 - Shashan Sooriyahetti
1
@hainabaraka 你应该立即将这个答案标记为已接受的答案,这是一个真正精彩的解释。 - chai

0

由于 allow_credentials=True 不允许使用通配符,请移除通配符:

app.add_middleware(
  CORSMiddleware,
  allow_origins=['http://localhost:8080'],
  allow_credentials=True,
  allow_methods=["GET", "POST", "OPTIONS"], /* include additional methods as per the application demand */
  allow_headers=["Content-Type","Set-Cookie"], /* include additional headers as per the application demand */
)

在设置 cookie 时,将 samesite 设置为 none(现代浏览器要求这样做):

/* `secure=True` is optional and used for secure https connections */
response.set_cookie(key='token_name', value='token_value', httponly=True, secure=True, samesite='none')

如果客户端使用Safari浏览器,请在“偏好设置”中禁用“防止跨站点跟踪”。就这样!

0

您可以通过访问 "chrome://flags" 并禁用 "Cookies without SameSite must be secure" 来完全禁用此功能。但是,这将禁用所有站点的功能,因此在您不进行开发时,安全性会降低。


7
这没用,我仍然收到“安全”错误提示。 - consoleart

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