之前在为其他业务部门提供标准的用户登录态鉴权及刷新操作SDK包时,有反馈说用户过一段时间登录态鉴权就不通过了,通过排查,发现了Golang在操作Cookie时,有一处怪异的操作,我们先来看看http包里的SetCookie操作都做了什么事情。

从上面的代码,我们可以看到,在SetCookie的时候,是先将Cookie结构体转成Cookie字符串,而在转字符串的时,如果Domain有值,他会有这样一个操作:当Domain的值以“.”开头时,他会将前面的“.”去掉,这样就和我们期望写入的Cookie的Domain不一样。

那么前面带点与不带点,有什么区别呢?这个将在专栏下另外的文章得到答案。

我们先来看看Cookie的结构:

从代码里,我们可以看到Cookie是有Name、Value、Domain、Path、Expires、Secure、HttpOnly、SameSite这几个主要属性,他们分别代表如下意思:
Name:就是当前Cookie的字段名称,我们在获取Cookie的Value时要通过该名称获取。
Value:当前Cookie的值,就是我们需要存储的信息。
Domain:该Cookie的作用域。
Path:该Cookie的作用路径。
Expires:该Cookie的过期时间,若不设置,则为会话级Cookie,会话结束即失效。
Secure:该Cookie是否只允许在Https下传输。
HttpOnly:该Cookie只允许在http请求下获取,前端页面不能通过JS脚本获取。
SameSite:是从谷歌70版本后新增的属性,用于标识该Cookie的跨域安全等级。

而我们上面说到的是Domain里是不是以“.”开头,那么我们着重说一下Cookie中Domain属性里不同值的区别:

1、不设置Domain属性:

当SetCookie时,不传入Domain,浏览器就会自动使用当前访问的域名作为Domian。如当前访问的是“https:\/\/second\.abc\.com\/index\.html”时,设置的Cookie就会写入“second\.abc\.com”下。

2、设置Domain属性:

当传入了Domain,此时浏览器就会根据传入的Domain,以及当前访问的URL里的Domain进行判断,如果传入的Domain是当前访问的Domain,或者是当前Domain的上级Domain,就会按传入的Domain进行写入,否则不给写入。如当前访问的是“https:\/\/second\.abc\.com\/index\.html”时,Domain可以传入“\.second\.abc\.com”、“second\.abc\.com”、“\.abc\.com”。(这几个Domain有什么区别,可以看专栏下另外的文章)

那么在Golang的代码里,当我们传入的是“\.second\.abc\.com”时,会自动帮我们转成“second\.abc\.com”,这个操作会带来什么后果呢?

这就需要说说Cookie的特性,对于Cookie,如果Name、Domain、Path不一样时,SetCookie时,都会写入一个新的Cookie记录;当这三个属性完全一样时,才会覆盖原来的记录,写入新的值。当存在多个记录,且Name为同一个时,后端通过http.Request.Cookies()能读到多条记录,而使用http.Request.Cookie时,会返回第一条记录,这时就会造成读取Cookie错乱,有可能读不到我们想要的那个Cookie。(PHP的$_COOKIE["name"]也有同样的问题)

也不清楚为什么Go的开发者要这样设计,所以,我们建议,在Go语言里,使用原生的Header方法设置完整的Cookie字符串来写入Cookie,以免给自己带来不必要的麻烦。