英文:
Cannot set cookie in client from a Go API
问题
我有一个用Go编写的后端,托管在Heroku上,我们称之为https://foo.herokuapp.com
。我有一个托管在不同域名上的前端,我们称之为https://ui.example.com
。
后端API有一个/api/user/login
的端点,它会以cookie的形式返回一个JSON Web Token,代码如下所示:
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: token, // JWT
HttpOnly: false, // 测试时设置为true,修复后再改为true
MaxAge: int(time.Hour * 24 * 3),
Expires: time.Now().UTC().Add(time.Hour * 24 * 3),
Path: "/",
Secure: true,
SameSite: http.SameSiteNoneMode,
})
这是我在服务器上的CORS设置:
crossOrigin := cors.New(cors.Options{
AllowedOrigins: []string{allowedOrigin},
AllowCredentials: true,
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut},
})
前端向后端发起请求的代码如下:
const endpoint = "/api/user/login/"
fetch(host + endpoint, {
method: "POST",
credentials: 'include',
body: JSON.stringify({
email,
password
})
}).then((response) => console.log(response))
.catch((err) => console.log(err));
问题:
现在,在浏览器的网络选项卡中可以看到这个cookie,但在应用程序选项卡(或Firefox中的存储选项卡中的cookie)中找不到这个cookie。浏览器没有保存这个cookie,这导致后续的请求失败,因为在处理实际请求之前,会验证和解码cookie中的令牌。
在另一个相关的主题中,我了解到Heroku在到达我的应用程序之前终止了SSL。因此,无法为非SSL流量设置安全cookie。那个解决方案建议信任X-Forwarded-For
中找到的方案。我使用https://github.com/gorilla/handlers包启用了这个功能,代码如下所示:
// srv是我的实际处理程序
// crossOrigin是CORS中间件
// 最后由ProxyHeaders中间件包装
handlers.ProxyHeaders(crossOrigin.Handler(srv))
然而,这并没有起作用。
我阅读了许多主题/博客,到目前为止都没有起作用。我做错了什么?
英文:
I have a backend written in Go, hosted on Heroku, let's call it, https://foo.herokuapp.com
. I have a frontend hosted on a different domain, let's call it, https://ui.example.com
.
The backend API has an endpoint /api/user/login
which sends back a JSON Web Token in the form of cookie shown as below:
http.SetCookie(w, &http.Cookie{
Name: "token",
Value: token, // the JWT
HttpOnly: false, // for testing, set to true later once I fix this
MaxAge: int(time.Hour * 24 * 3),
Expires: time.Now().UTC().Add(time.Hour * 24 * 3),
Path: "/",
Secure: true,
SameSite: http.SameSiteNoneMode,
})
These are my CORS settings on the server.
crossOrigin := cors.New(cors.Options{
AllowedOrigins: []string{allowedOrigin},
AllowCredentials: true,
AllowedMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut},
})
The frontend makes a request to the backend as given below.
const endpoint = "/api/user/login/"
fetch(host + endpoint, {
method: "POST",
credentials: 'include',
body: JSON.stringify({
email,
password
})
}).then((response) => console.log(response))
.catch((err) => console.log(err));
PROBLEM:
Now this cookie is actually visible in my browser's Network tab.
But the cookie does not exist in the application tab (or Storage tab in firefox where cookies exist). The browser is not saving the cookie, which is causing the subsequent requests to fail as the token in the cookie is verified and decoded before processing the actual request.
In another somewhat related thread, I got to know that Heroku terminates SSL before reaching my app. And, thus secure cookies cannot be set for non-SSL traffic. The solution there suggests trusting the scheme found in X-Forwarded-For
. I enabled that using using the https://github.com/gorilla/handlers package as follows.
// srv is my actual handler
// crossOrigin is the CORS middleware
// finally wrapped by ProxyHeaders middleware
handlers.ProxyHeaders(crossOrigin.Handler(srv))
Yet this is not working.
I read many threads/blogs. Nothing has worked so far. What am I doing wrong?
答案1
得分: 3
Cookie是由浏览器保存的,保存在设置该Cookie的域名下。仅仅因为你在应用程序选项卡中看不到Cookie,并不意味着Cookie没有被保存。
如果你的前端https://ui.example.com
发起了一个XHR调用到https://foo.herokuapp.com
,并且该调用返回了一个Set-Cookie
头部,那么浏览器会将该Cookie保存在foo.herokuapp.com
域名下。你在ui.example.com
的应用程序选项卡中看不到它。然而,当你再次向foo.herokuapp.com
发起XHR调用时,浏览器会发送之前设置的Cookie。
你可以进行如下实验:登录后,打开一个新的标签页并导航到https://foo.herokuapp.com
。现在打开应用程序选项卡,你应该在那里看到你的Cookie。
话虽如此,要记住浏览器会将这些Cookie视为第三方Cookie,浏览器供应商最终会停止支持第三方Cookie。最终,你应该确保你的前端和后端是从同一个父域名提供的。
至于另一个问题 - Heroku在其网关和你的应用程序之间终止SSL并不是一个问题。Cookie上的secure
标志是给浏览器的信息 - 浏览器不会在非SSL连接上接受或发送带有此标志的Cookie。你的浏览器与Heroku服务器之间的连接是SSL的,所以Cookie会被接受/发送。在后端,Cookie只是HTTP头部,后端对Cookie的标志和连接类型并不关心。
英文:
Cookies are saved by the browser for the domain which set the cookie in the first place. Only becasue you can't see the cookie in the Application tab, does not mean that the cookie wasn't saved.
If your frontend https://ui.example.com
makes an XHR call to https://foo.herokuapp.com
and that call returns a Set-Cookie
header, then the browser saves that cookie under foo.herokuapp.com
domain. You will not see it in the ui.example.com
's Application tab. Still, when you make another XHR call to foo.herokuapp.com
then the browser will send the cookies that you've set earlier.
You can make this experiment: After logging in, open a new tab and navigate to https://foo.herokuapp.com
. Now open the Application tab and you should see your cookies there.
That said, remember that the browser will treat these cookies as 3rd party cookies, and browser vendors will eventually drop support for 3rd party cookies. Eventually you should make sure that your frontend and backend are served from the same parent domain.
As for the other problem - Heroku's termination of SSL between their gateway and your app is not a problem. The secure
flag on a cookie is an information for the browser - the browser will not accept or send a cookie with this flag over a non-SSL connection. The connection between your browser and the heroku server is SSL, so cookies will be accepted/sent. In your backend, cookies are just HTTP headers, and the backend does not really care neither about the cookies' flags nor by the connection type.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论