我们访问网站,为什么每次打开都得登录?

这个问题我刚入行的时候压根没想过——直到有天要排查一个”用户突然掉登录态”的bug,才发现自己对这俩东西的理解还不如一个实习生。

为什么要有登录态这回事

打开百度,搜个东西,关掉。再打开,再搜——百度根本不关心你是谁。

但你打开淘宝,想加个购物车、下个单,服务器就必须知道:这个请求是张三发的,不是李四

问题来了:HTTP协议天生不认识人。你发第一个请求和第二个请求,在服务器眼里就是两个陌生人。所以我们要搞一个”身份证明”——这就是登录态。

服务端怎么认出你

你登录的时候,服务器干了这么几件事:

  1. 验证账号密码
  2. 在自己内存(或数据库、Redis)里划一块地方,存上你的身份信息
  3. 给这块地方一个唯一编号,比如 sessionId: "a1b2c3d4"
  4. 把这个编号塞进响应里,发回给浏览器

这个存在服务端的信息集合,就叫 Session

简单粗暴,session.getId() 一调,服务端就知道你是谁了。

Cookie:浏览器的”身份证夹子”

编号发回来了,浏览器怎么存?下次请求怎么带过去?

答案是 Cookie

服务端在响应头里加一行 Set-Cookie: JSESSIONID=a1b2c3d4,浏览器就会把这个键值对存下来。之后每次往同一个站点发请求,浏览器会自动在请求头里带上 Cookie: JSESSIONID=a1b2c3d4

浏览器自动带的,不需要你写任何代码。 这就是cookie最省心的地方,也是最容易踩坑的地方。

所以你看,session是服务端的账本,cookie是客户端的收据。一个记账,一个传话。

Cookie的安全那些事

既然cookie这么重要,随便让别人偷了可不行。几个关键属性一定得知道:

  • HttpOnly:设了这个,JavaScript就读不到cookie了。有人XSS注入想偷你cookie?读不到。这个是必加的,没得商量。
  • Domain:cookie属于哪个域名。设成 .taobao.com,那么 www.taobao.comm.taobao.com 都能读到。设错了?子域名之间互相认不出来。
  • Path:cookie在哪个路径下生效。设成 /api,那访问 /web 时就不会带这个cookie。范围越小越安全。

还有个 Secure 属性,设了之后只在HTTPS下传输。明文HTTP?不好意思,浏览器不带。

CSRF:一句话说清楚

既然浏览器会自动带cookie,那如果有人在别的网站诱导你的浏览器偷偷向淘宝发请求——cookie照带不误,淘宝还以为是你本人操作。

这就是CSRF:浏览器太老实,别人让它发啥它就发啥,cookie还自动附赠。

解决办法一般是加个token验证,确认请求确实是从自家页面发出来的。

最后

session和cookie这对好兄弟,一个管记账一个管传话,配合了几十年没出过大问题。但每次出bug的时候你去看,十有八九是cookie的domain没设对,或者session过期时间和cookie没对齐。

数据不会骗人,但cookie的默认行为经常吓人。