Skip to content

Cookies

元信息

  • 目标:解释"登录状态为何能跨请求保持"——理解 cookie 的读写机制、随 HTTP 头交换的流程、会话失效与同源保护;能分辨 cookie 与 token 两种状态方案的适用场景
  • 关键概念:cookie、session_id、Set-Cookie、浏览器会话、Expires、同源策略、token
  • 关联阶段:cp3
  • 常见误区:以为 cookie 是服务端写进浏览器的数据库(它只是按域名隔离的小文本,客户端也能读写);以为不设过期时间就永久有效(会话 cookie 关浏览器即删);以为换台机器登录状态还在(cookie 不跨设备);以为任何网站的脚本都能读走 cookie(同源策略限定仅同源可读)

前情回顾

  • DOM API : 基于js操纵网页(客户端数据)的能力
    • 事件 event: 页面交互机制,用户->程序员->浏览器
  • Ajax: 基于js发起通讯(服务端数据)的能力
    • 回调 callback: 默认的网络事件处理函数,客户端-程序员-->服务端

问题

  • 有一些网站,一旦登录,后续再访问该网站,都保持着登录状态。 然而,当你关闭重启浏览器后(或者过一段较长的时间后),就需要你重新登录
  • 有一些网站的登录页面,会有一个复选框:记住我一天、一月、一年。当你选中后,即使你关闭浏览器,也不需要重新登录
    • 但银行、支付宝比较安全的网站,通常提示:公共场合不要复选它
    • 但是,当你换一台机器后,你又需要重新登录

猜想:状态与存储

  • 网页一定是在我的电脑里留下什么标记?这些标记并且会定时被清除
  • 但是:在浏览器中运行的网页真的能够向我的硬盘里写入数据吗?
    • WEB应用为何优于桌面软件,就是因为:安全、安全、安全

什么是 cookies

  • 允许浏览器可以读写本地硬盘中一个特殊目录下的特殊文件,即 cookies 文件
  • 浏览器默认为每一个域名生成一个cookies文件,并且保证该文件只能被同一域名的页面所读取

使用浏览器的开发者工具(F12)

  • application面板:cookies节点中可查看对应域名在本地的存储信息
  • network 面板,查看 http 请求的 request和response头信息中的set-cookie和cookie头,可查看其交换机制

cookies的交换机制:服务端发起

  • 当Web服务器收到一个客户端请求,要求访问受保护的服务端资源(即需要登录)
  • 服务端脚本检查 http request header中是否存在cookies字段,如不存在,则发起一个服务端重定向,指引浏览器转到登录页面
  • 用户填写登录表单,即账户信息后,提交给服务端
  • 服务端脚本验证账户信息,如成功,则为该账户生成一个随机码作为凭证,一般记为session_id,并保存到服务端的数据库中,如sessions表中。
  • 同时,服务端脚本在 http response header中插入一个set cookies字段,其值即为上述的session_id
  • 浏览器收到服务端的响应后,会检查 http response header 中是否存在set cookies字段,如有,则将其保存到本地cookies文件或数据库中
  • 每当浏览器发起请求时,都会检查目标网站域名是否存在相应的本地cookies数据,如存在,则将其附加在http request header中的cookies字段,发送给服务端
  • 服务端脚本收到脚本后,取出cookies的值,并在服务端sessions表中查询,如匹配,则表明该请求来自已登录的用户,同时从sessions表中取出用户名、角色或权限等内容,判定访问权限。

cookies的交换机制:客户端发起

  • 客户端也可以创建cookies,即直接修改本地cookies数据
  • 在后续的客户端请求中,这些cookies数据仍然通过cookes字段被发送到服务端

客户端创建的cookies通常用于存储临时购物车、收藏夹这类功能,而不用于登录,为什么?

set-cookies 服务端使用 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Reference/Headers/Set-Cookie

cookies 客户端使用 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Reference/Headers/Cookie

cookies的存储位置:现代浏览器

采用文件型数据库,如sqlite,易于管理(chrome://settings/siteData ),但不易查看

  • chrome
    • C:\Users[username]\AppData\Local\Google\Chrome\User Data\Default\Network
  • edge for win10/11
    • %LOCALAPPDATA%\Microsoft\Edge\User Data\Default\Network
  • firefox
    • %APPDATA%\Mozilla\Firefox\Profiles

https://www.thewindowsclub.com/cookies-folder-location-windows

cookies的存储位置:IE浏览器

采用文本文件方式存储,易于观察,但不易于管理

注意:需要设置显示已隐藏的文件夹 https://www.cnblogs.com/gdyblog/p/5874362.html

参考 https://www.thewindowsclub.com/cookies-folder-location-windows

cookies的结构

  • Name: Cookie名称,必须使用只能用在URL中的字符,一般用字母及数字
  • Value: Cookie值, 同样也只能使用可以用在URL中的字符,一般需要对其使用encodeURI方法进行转义
  • Expires: 过期时间,一个GMT格式的时间
  • Path: 路径,在这个路径下面的页面才可以访问该Cookie,一般设为“/”,以表示同一个站点的所有页面都可以访问这个Cookie
  • Domain: 子域,指定在该子域下才可以访问Cookie,例如要允许Cookie在 bbs.x.com 下可以访问,但在 www.x.com 下不能访问,则需要将domain设置成bbs.x.com
  • Secure: 安全性,指定Cookie是否只能通过https协议访问,一般的Cookie使用HTTP协议既可访问,如果设置了Secure(空值即可),则只有当使用https协议连接时cookie才可以被页面访问

cookies 的读与写

javascript
document.cookie = "userName=" + encodeURI("tom") + 
     "; expires=" + (new Date("2022-01-01")).toUTCString() + 
     "; path=/; domain=x.com; secure";

console.log(document.cookie);

什么是登录和登出?

  • 当用户通过浏览器访问网页时,浏览器将登录信息记录在cookies,并持续发送给服务端脚本
  • 当用户不希望浏览器记录自己的访问身份信息时,可禁止浏览器记录(浏览器设置:禁用cookies)或删除掉已存有登录信息的cookies

浏览器会话: cookies的默认失效机制

  • 关闭当前登录的浏览器标签窗口,再打开另一个,是否需要重新登录?
  • 关闭浏览器的所有窗口标签时,重新打开浏览器,是否需要重新登录?
  • cookies的默认设置:如果未对cookies设置失效时间,则在浏览器会话关闭后,浏览器自动删除该cookies

浏览器会话: 浏览器窗口进程的一个生命周期

自定义会话:显示设置失效时间

  • 定时登出:登录脚本设置一个有限的未来时间,当时间到达,浏览器将自动删除cookies
  • 用户登出: 登录脚本设置一个无限大的未来时间,只有当用户点击登出即执行登出脚本时,登出脚本将设置一个负失效时间(比当前系统时间更早的时间值),浏览器执行脚本时将立即删除该cookies
    • 或者浏览器端的cookies被清除(如浏览器重装、系统重装等)

同源: cookies的安全机制

  • http://www.a.com登录,用户信息被该站页面的脚本写入cookies。随后,用户又访问了http://www.b.com,请b.com的网页中的脚本是否能读取到含有a.com用户的登录信息?
  • 如果用户随后访问的是http://bbs.a.com,请问能否读取到?
  • 如果用户随后访问的是https://www.a.com,请问能否读取到?

同源策略: 浏览器的一种安全机制,客户端脚本在没有明确授权的情况下,只能读写与自身同一来源的资源,如cookies等

教程

http://www.runoob.com/js/js-cookies.html

工具

扩展阅读:token

另一种保持状态的机制:客户端发起的每一个请求,即所有的链接和表单中,都需要附带一个参数,形如:

<!DOCTYPE html>
<html>
<head>
  <title>Parcel Sandbox</title>
  <meta charset="UTF-8" />
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <h1>Hello world</h1>
</body>
</html>

但上述方式的处理较为不便,需要服务端脚本成生成页面时,对每一个链接和表单进行处理。

另一种方式是将状态变量(即上述的user_id)保存在http数据包的字段中,类似于cookies的交换和传输:

  • 当登录验证通过后,服务端脚本在http响应包中插入一个自定义的字段,比如x-token
  • 客户端脚本在收到http响应包后,将其暂存在本地存储中(见后),当发起下一个http请求时,则将该字段的值,附加到http请求包中,发给服务端
  • 服务端脚本收到客户端请求时,如果从http请求包中检测到x-token字段,并且其值有效(在已登录的用户列表中存在相同的值),则表明该请求是登录过的用户发来的,从而给予相应权限。

Token vs Cookies

  • Cookies 是浏览器默认支持的状态交换协议,且包含了存储的功能。优点是方便,但受到协议的限制。适合于轻客户端+重服务端的应用,如服务端驱动的应用。
  • Token 则是开发者自行定义的状态交换协议,且不与存储功能绑定。优点是易于扩展,缺点则是需要开发者提供实现机制,如前端、后端脚本的同步传输和存储。适合于重js或重客户端的应用,如单页SPA应用。

more