记录 Cookie 用法
基本概念
Cookie 作为 Web 交互中与客户关联的数据由 Server 端生成,在 Browser 客户端端保存,必要的时候由 Browser 发送给服务端。
- Cookie 的传输过程会存放在 Http 的 Header 中
一般流程
- 客户端(Browser)访问 Server,此时 request header 中不携带 Cookie
- 服务端根据 request 中的数据,生成和客户相关的数据,放入 Cookie,在返回的 response 包含了 header
Set-Cookie:...
- 客户端收到 response 后,按照 Set-Cookie 指定的参数将 Cookie 保存
- 客户端下一次向 Server 发送 request 时,会自动携带 header
Cookie:...
- Server 端收到后解析 Cookie 内容,取得用户相关数据,做相应的处理后,返回
Cookie 选项
Cookie 相当于在客户端保存一个 map(key-value)
- Name 必填,相当于 Key
- Value 必填,相当于 Value
- Path 默认为当前 request 的 path
- Domain 默认为当前 request 的 Domain
- Expires 过期时间点,默认参照 MaxAge
- MaxAge
秒级存活时间,默认 -1
<0
只在当前浏览器打开时起作用,浏览器关闭就删除=0
从客户端删除此 Cookie>0
存活指定的秒数后删除
- Secure bool 型,是否只在 https 下才能使用,以免 http 下被”中间人”窃取。 默认 false
- HttpOnly bool 型,是否只允许 http 发送时使用,以免 js 获取秘密信息。 默认 false
- SameSite
限制使用 Cookie 的域名,用来防止 CSRF 攻击。
默认 Lax
- Strict 完全禁止第三方 Cookie,跨域时,任何情况下都不发送
- Lax 允许向目标网址的 Get 请求中使用 Cookie,其它情况不允许
- None 没有任何限制,可以跨域使用
跨域问题
通常情况下,A URI 自己设置的 CookieA 会绑定到自己的 DomainA 下,之后 A 的 javascript/http 都能使用该 CookieA。 但是有时需要 B Domain 下的 js 操纵 CookieA,或者 B Domain 下的 js 向 A 发送 http 请求时想要带上 CookieA,就需要特殊处理了。
-
同根域名跨域 如果是”a.test.com”设置了 Cookie,而”b.test.com”想访问,可以在设置 Cookie 时指定 domain “.test.com”,这样 “b.test.com” 就可以合法访问了。 注意设置 domain 时,前面要有 “.”
-
跨域 js 访问 假设”a.com”设置了 Cookie,而”b.com”的 js 想访问,可以设置
HttpOnly=false; SameSite=None;
,这样就能访问了。 -
跨域 http 发送 假设”a.com”设置了 CookieA,而”b.com”的 js 向”a.com/xxx”发送 request 时想携带 CookieA. 网络协议新增了 header
Access-Control-Allow-Credentials
,表示允许携带跨域的隐私信息,使用方法如下:- “b.com” 的 js 向”a.com/xxx”发送 request 时要设置
request.withCredentials=true
,这时 request 会携带 headerOrigin:https://b.com
, 同时协议标记strict-origin-when-cross-origin
- “b.com/xxx” 要提前配置
Access-Control-Allow-Origin: https://b.com
+Access-Control-Allow-Credentials: true
+Access-Control-Allow-Methods:OPTIONS, GET, POST...
, - “b.com/xxx” 返回 response,浏览器比对 OK 后,向 js 返回 response;如果服务端返回 header 不匹配,则报 “CORS error” 表示跨域安全存在问题不允许读取 response。
- 注意点:
- 通常我们会设置
Access-Control-Allow-Origin: *
来允许所有 origin 跨域,但这种设置和withCredentials
是冲突的,必须设置指定的 origin - origin 包含的 schema 为:
protocal://host[:port]
,例https://b.com
- 开启”withCredentials”后,除非一次可
- 带预检(Preflighted)的跨域请求,当 HTTP 请求出现以下两种情况时,浏览器认为是带预检(Preflighted)的跨域请求:
- 除 GET、HEAD 和 POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他 HTTP 方法。
- 请求中出现自定义 HTTP 头部。
- Preflighted 会分两步进行请求处理:
先发送一个
OPTIONS
类型的询问 request,如果 response 完全匹配,再发送 real request。这就是为什么上面必须有Access-Control-Allow-Methods:OPTIONS
- 通常我们会设置
- “b.com” 的 js 向”a.com/xxx”发送 request 时要设置
-
nginx 解决跨域 http 发送 跨域 http 时,可以用 nginx 配置,将两个域名模拟在同一根域名下。但随着
withCredentials
标准的出现,已经不需要这种 hack 方式了。
JWT
JWT(Json Web Token)是为了网络间传递声明的一种 JSON 开放标准(rfc7519),适用于 SSO 等认证加密场景。 例如”a.com”想保存一些隐私信息到 Cookie、允许其他域名 http 发送,如果用 JSON 明文或 Base64 编码保存到 Cookie Value 中,则在浏览器就可以直接查看,泄露了隐私。 这时就可以用 JWT 做对称加密,Cookie 中的 Value 数据只有”a.com”的服务端可以解密使用。
- JWT 基本流程:
- “a.com”服务端生成一个秘钥secret,该秘钥只在服务端保存,用于对称加密
- 第一次访问”a.com”时,利用 JSON 将 object 生成需要加密的 TokenValue,对其用 hs256 等对称方式加密,生成 code1,对 code1 进行 base64 编码,生成 code2,将 code2 设置为 TokenValue
- “a.com”/”b.com” 发送 http 请求到 “a.com” 时,携带包含 code2 的 Cookie
- “a.com”服务端解码 base64、hs256 解密、json.Unmarshal,得到真正的 object,然后进行逻辑处理