阿卡不拉阿卡不拉
Vue3
阿卡的博客
Vue3
阿卡的博客
  • Vue3

    • 快速入门

      • 搭建工程 👌
      • 模板语法
      • 响应式基础
      • 响应式常用 API
      • 计算属性
      • 类与样式绑定
      • 条件和列表渲染
      • 事件处理
      • 表单处理
      • 生命周期
      • 侦听器
      • 组件介绍
      • Props
      • 自定义事件
      • 组件v-model
      • 插槽
      • 前端路由介绍
      • KeepAlive内置组件
      • 状态管理库
      • 组件库介绍
    • 深入本质

      • 虚拟DOM本质
      • 模板的本质
      • 组件树和虚拟DOM树
      • 数据拦截的本质
      • 响应式数据的本质
      • 响应式的本质
      • 响应式和组件渲染
      • 实现响应式系统 1
      • 实现响应式系统 2
      • 图解EFFECT
      • 实现响应式系统 3
      • 手写computed
      • 手写watch
      • 指令的本质
      • 插槽的本质
      • v-model的本质
      • setup 语法标签
      • 组件生命周期
      • keepalive 生命周期
      • keepalive的本质
      • key的本质
    • 细节补充

      • 【Vue】属性透传
      • 【Vue】依赖注入
      • 【Vue】组合式函数 👌
      • 【Vue】自定义指令
      • 【Vue】插件
      • 【Vue】Transition
      • 【Vue】TransitionGroup
      • 【Vue】Teleport
      • 【Vue】异步组件
      • 【Vue】Suspense
      • 【Router】路由模式
      • 【Router】路由零碎知识
      • 【Router】路由匹配语法
      • 【Router】路由组件传参
      • 【Router】内置组件和函数
      • 【Router】导航守卫
      • 【Router】过渡特效
      • 【Router】滚动行为
      • 【Router】动态路由
      • 【State】通信方式总结
      • 【State】Pinia 自定义插件
      • 【场景】封装树形组件
      • 【场景】自定义 ref 实现防抖
      • 【场景】懒加载
      • 【场景】虚拟列表
      • 【场景】虚拟列表优化
      • 【第三方库】VueUse
      • 【第三方库】vuedragable
      • 【第三方库】vue-drag-resize
      • 【第三方库】vue-chartjs
      • 【第三方库】vuelidate
      • 【第三方库】vue3-lazyload
      • 【场景】Websocket 聊天室
      • 【Vite】✨ 认识 Vite👌
      • 【Vite】配置文件 👌
      • 【Vite】✨ 依赖预构建 👌
      • 【Vite】构建生产版本 👌
      • 【Vite】环境变量与模式
      • 【Vite】CLI
      • 【Vite】Vite 插件
  • 笔面试

    • HTML

      • HTML 面试题汇总
      • 文档声明
      • 语义化
      • W3C 标准组织
      • SEO
      • iframe
      • 微格式
      • 替换元素
      • 页面可见性
    • CSS

      • CSS 面试题汇总
      • CSS 单位总结
      • 居中方式总结
      • 隐藏元素方式总结
      • 浮动
      • 定位总结
      • BFC
      • CSS 属性计算过程
      • CSS 层叠继承规则总结
      • @import 指令
      • CSS3 calc 函数
      • CSS3 媒体查询
      • 过渡和动画事件
      • 渐进增强和优雅降级
      • CSS3 变形
      • 渐进式渲染
      • CSS 渲染性能优化
      • 层叠上下文
      • CSS3 遮罩
    • JavaScript

      • JavaScript 面试题汇总
      • ✨ let、var、const 的区别
      • JS中的数据类型
      • 包装类型
      • 数据类型的转换
      • 运算符
      • ✨ 原型链
      • ✨ this 指向
      • ✨ 垃圾回收与内存泄漏
      • ✨ 执行栈和执行上下文
      • ✨ 作用域和作用域链
      • ✨ 闭包
      • DOM 事件的注册和移除
      • DOM 事件的传播机制
      • 阻止事件默认行为
      • 递归
      • ✨ 属性描述符
      • class 和构造函数区别
      • 浮点数精度问题
      • 严格模式
      • ✨ 函数防抖和节流
      • ✨ WeakSet 和 WeakMap
      • ✨ 深浅拷贝
      • 函数柯里化
      • Node 事件循环
      • 尺寸和位置
    • 浏览器

      • 浏览器面试题汇总
      • ✨ 浏览器的渲染流程
      • ✨ 资源提示关键词
      • 浏览器的组成部分
      • IndexedDB
      • ✨ File API
      • ✨ 浏览器缓存
      • ✨ 浏览器跨标签页通信
      • Web Worker
    • 网络

      • 网络面试题汇总
      • 五层网络模型 👌
      • 常见请求方法 👌
      • ✨cookie👌
      • 面试题
      • 加密
      • ✨JWT👌
      • ✨ 同源策略及跨域问题 👌
      • 文件上传
      • ✨ 输入 url 地址之后
      • 文件下载
      • ✨ session
      • ✨ TCP
      • ✨ CSRF 攻击
      • ✨XSS 攻击 👌
      • ✨ 网络性能优化
      • 断点续传
      • 域名和 DNS
      • SSL、TLS、HTTPS 的关系
      • ✨ HTTP 各版本差异 👌
      • HTTP 缓存协议
      • ✨ WebSocket
    • 工程化

      • CMJ 和 ESM
      • npx
      • ESLint
    • Vue2

      • Vue 面试题汇总相关
      • 组件通信方式总结
      • 虚拟 DOM
      • v-model
      • 数据响应式原理
      • diff
      • 生命周期详解
      • computed
      • 过滤器
      • 作用域插槽
      • 过度和动画
      • 优化
      • keep-alive
      • 长列表优化
      • 其他 API
      • 模式和环境变量
      • 更多配置
      • 更多命令
      • 嵌套路由
      • 路由切换动画
    • Vue3

      • ✨ Vue3 整体变化 👌
      • ✨ Vue3 响应式变化 👌
      • ✨ nextTick 实现原理 👌
      • 两道代码题 👌
      • Vue 运行机制
      • 渲染器核心功能
      • 事件绑定与更新

✨cookie👌

一个不大不小的问题

假设服务器有一个接口,通过请求这个接口,可以添加一个管理员

但是,不是任何人都有权力做这种操作的

那么服务器如何知道请求接口的人是有权力的呢?

答案是:只有登录过的管理员才能做这种操作

可问题是,客户端和服务器的传输使用的是 http 协议,http 协议是无状态的,什么叫无状态,就是每个 HTTP 请求都是完全独立的,服务器不会“记住”之前的请求

为什么 http 协议是无状态的?

http 协议被设计成无状态的协议主核心原因如下:

  1. 简化服务器设计:无需分配内存存储用户状态
  2. 高并发处理能力:服务器处理完请求后立即释放资源
  3. 增强可扩展性:直接添加新服务器就可以进行水平扩展

由于 http 协议的无状态,服务器忘记了之前的所有请求,它无法确定这一次请求的客户端,就是之前登录成功的那个客户端。

你可以把服务器想象成有着严重脸盲症的东哥,他没有办法分清楚跟他说话的人之前做过什么

于是,服务器想了一个办法

它按照下面的流程来认证客户端的身份

  1. 客户端登录成功后,服务器会给客户端一个出入证
  2. 后续客户端的每次请求,都必须要附带这个出入证

服务器发扬了认证不认人的优良传统,就可以很轻松的识别身份了。

但是,用户不可能只在一个网站登录,于是客户端会收到来自各个网站的出入证,因此,就要求客户端要有一个类似于卡包的东西,能够具备下面的功能:

  1. 能够存放多个出入证。这些出入证来自不同的网站,也可能是一个网站有多个出入证,分别用于出入不同的地方
  2. 能够自动出示出入证。客户端在访问不同的网站时,能够自动的把对应的出入证附带请求发送出去。
  3. 正确的出示出入证。客户端不能将肯德基的出入证发送给麦当劳。
  4. 管理出入证的有效期。客户端要能够自动的发现那些已经过期的出入证,并把它从卡包内移除。

能够满足上面所有要求的,就是 cookie

cookie 类似于一个卡包,专门用于存放各种出入证,并有着一套机制来自动管理这些证件。

卡包内的每一张卡片,称之为一个 cookie。

cookie 的组成

cookie 是浏览器中特有的一个概念,它就像浏览器的专属卡包,管理着各个网站的身份信息。

每个 cookie 就相当于是属于某个网站的一个卡片,它记录了下面的信息:

  • key:键,比如「身份编号」
  • value:值,比如阿卡的身份编号「14563D1550F2F76D69ECBF4DD54ABC95」,这有点像卡片的条形码,当然,它可以是任何信息
  • domain:域,表达这个 cookie 是属于哪个网站的,比如forclh.top,表示这个 cookie 是属于forclh.top这个网站的
  • path:路径,表达这个 cookie 是属于该网站的哪个基路径的,就好比是同一家公司不同部门会颁发不同的出入证。比如/news,表示这个 cookie 属于/news这个路径的。(后续详细解释)
  • secure:是否使用安全传输(后续详细解释)
  • expire:过期时间,表示该 cookie 在什么时候过期

当浏览器向服务器发送一个请求的时候,它会瞄一眼自己的卡包,看看哪些卡片适合附带捎给服务器

如果一个 cookie同时满足以下条件,则这个 cookie 会被附带到请求中

  • cookie 没有过期
  • cookie 中的域和这次请求的域是匹配的
    • 比如 cookie 中的域是forclh.top,则可以匹配的请求域是forclh.top、www.forclh.top、blogs.forclh.top等等
    • 比如 cookie 中的域是www.forclh.top,则只能匹配www.forclh.top这样的请求域
    • cookie 是不在乎端口的,只要域匹配即可
  • cookie 中的 path 和这次请求的 path 是匹配的
    • 比如 cookie 中的 path 是/news,则可以匹配的请求路径可以是/news、/news/detail、/news/a/b/c等等,但不能匹配/blogs
    • 如果 cookie 的 path 是/,可以想象,能够匹配所有的路径
  • 验证 cookie 的安全传输
    • 如果 cookie 的 secure 属性是 true,则请求协议必须是https,否则不会发送该 cookie
    • 如果 cookie 的 secure 属性是 false,则请求协议可以是http,也可以是https

如果一个 cookie 满足了上述的所有条件,则浏览器会把它自动加入到这次请求中

具体加入的方式是,浏览器会将符合条件的 cookie,自动放置到请求头中,例如,当我在浏览器中访问百度的时候,它在请求头中附带了下面的 cookie:

看到打马赛克的地方了吗?这部分就是通过请求头cookie发送到服务器的,它的格式是键=值; 键=值; 键=值; ...,每一个键值对就是一个符合条件的 cookie。

如何设置 cookie

由于 cookie 是保存在浏览器端的,同时,很多证件又是服务器颁发的

所以,cookie 的设置有两种模式:

  • 服务器响应:这种模式是非常普遍的,当服务器决定给客户端颁发一个证件时,它会在响应的消息中包含 cookie,浏览器会自动的把 cookie 保存到卡包中
  • 客户端自行设置:这种模式少见一些,不过也有可能会发生,比如用户关闭了某个广告,并选择了「以后不要再弹出」,此时就可以把这种小信息直接通过浏览器的 JS 代码保存到 cookie 中。后续请求服务器时,服务器会看到客户端不想要再次弹出广告的 cookie,于是就不会再发送广告过来了。

服务器端设置 cookie

服务器可以通过设置响应头,来告诉浏览器应该如何设置 cookie

响应头按照下面的格式设置:

set-cookie: cookie1set-cookie: cookie2set-cookie: cookie3...

通过这种模式,就可以在一次响应中设置多个 cookie 了,具体设置多少个 cookie,设置什么 cookie,根据你的需要自行处理

其中,每个 cookie 的格式如下:

键=值; path=?; domain=?; expire=?; max-age=?; secure; httponly

每个 cookie 除了键值对是必须要设置的,其他的属性都是可选的,并且顺序不限

当这样的响应头到达客户端后,浏览器会自动的将 cookie 保存到卡包中,如果卡包中已经存在一模一样的卡片(其他 path、domain 相同),则会自动的覆盖之前的设置。

下面,依次说明每个属性值:

path

设置 cookie 的路径。如果不设置,浏览器会将其自动设置为当前请求的路径。比如,浏览器请求的地址是/login,服务器响应了一个set-cookie: a=1,浏览器会将该 cookie 的 path 设置为请求的路径/login

domain

设置 cookie 的域。如果不设置,浏览器会自动将其设置为当前的请求域,比如,浏览器请求的地址是 <http://www.forclh.top> ,服务器响应了一个 set-cookie: a=1 ,浏览器会将该 cookie 的 domain 设置为请求的域 www.forclh.top

  • 这里值得注意的是,如果服务器响应了一个无效的域,浏览器是不认的
  • 什么是无效的域?就是响应的域连根域都不一样。比如,浏览器请求的域是forclh.top,服务器响应的 cookie 是set-cookie: a=1; domain=baidu.com,这样的域浏览器是不认的。
  • 如果浏览器连这样的情况都允许,就意味着张三的服务器,有权利给用户一个 cookie,用于访问李四的服务器,这会造成很多安全性的问题

expire

设置 cookie 的过期时间。这里必须是一个有效的 GMT 时间,即格林威治标准时间字符串,比如Fri, 17 Apr 2020 09:35:59 GMT,表示格林威治时间的2020-04-17 09:35:59,即北京时间的2020-04-17 17:35:59。当客户端的时间达到这个时间点后,会自动销毁该 cookie。

max-age

设置 cookie 的相对有效期。expire 和 max-age 通常仅设置一个即可。比如设置max-age为1000,浏览器在添加 cookie 时,会自动设置它的expire为当前时间加上 1000 秒,作为过期时间。 - 如果不设置 expire,又没有设置 max-age,则表示会话结束后过期。对于大部分浏览器而言,关闭所有浏览器窗口意味着会话结束。

secure

设置 cookie 是否是安全连接。如果设置了该值,则表示该 cookie 后续只能随着https请求发送。如果不设置,则表示该 cookie 会随着所有请求发送。

httponly

设置 cookie 是否仅能用于传输。如果设置了该值,表示该 cookie 仅能用于传输,而不允许在客户端通过 JS 获取,这对防止跨站脚本攻击(XSS)会很有用。

  • 未设置 httponly

  • 设置了 httponly

举例

下面来一个例子,客户端通过post请求服务器http://forclh.top/login,并在消息体中给予了账号和密码,服务器验证登录成功后,在响应头中加入了以下内容:

set-cookie: token=123456; path=/; max-age=3600; httponly

当该响应到达浏览器后,浏览器会创建下面的 cookie:

key: token
value: 123456
domain: forclh.top
path: /
expire: 2020-04-17 18:55:00 #假设当前时间是2020-04-17 17:55:00
secure: false #任何请求都可以附带这个cookie,只要满足其他要求
httponly: true #不允许JS获取该cookie

于是,随着浏览器后续对服务器的请求,只要满足要求,这个 cookie 就会被附带到请求头中传给服务器:

cookie: token=123456; 其他cookie...

删除 cookie

现在,还剩下最后一个问题,就是如何删除浏览器的一个 cookie 呢?

如果要删除浏览器的 cookie,只需要让服务器响应一个同样的域、同样的路径、同样的 key,只是时间过期的 cookie 即可

所以,删除 cookie 其实就是修改 cookie

下面的响应会让浏览器删除token

set-cookie: token=; domain=forclh.top; path=/; max-age=-1

浏览器按照要求修改了 cookie 后,会发现 cookie 已经过期,于是自然就会删除了。

无论是修改还是删除,都要注意 cookie 的域和路径,因为完全可能存在域或路径不同,但 key 相同的 cookie

因此无法仅通过 key 确定是哪一个 cookie

客户端设置 cookie

既然 cookie 是存放在浏览器端的,所以浏览器向 JS 公开了接口,让其可以设置 cookie

document.cookie = "键=值; path=?; domain=?; expire=?; max-age=?; secure";

可以看出,在客户端设置 cookie,和服务器设置 cookie 的格式一样,只是有下面的不同

  • 没有 httponly。因为 httponly 本来就是为了限制在客户端访问的,既然你是在客户端配置,自然失去了限制的意义。
  • path 的默认值。在服务器端设置 cookie 时,如果没有写 path,使用的是请求的 path。而在客户端设置 cookie 时,也许根本没有请求发生。因此,path 在客户端设置时的默认值是当前网页的 path
  • domain 的默认值。和 path 同理,客户端设置时的默认值是当前网页的 domain
  • 其他:一样
  • 删除 cookie:和服务器也一样,修改 cookie 的过期时间即可

总结

以上,就是 cookie 原理部分的内容。

如果把它用于登录场景,就是如下的流程:

登录请求

  1. 浏览器发送请求到服务器,附带账号密码
  2. 服务器验证账号密码是否正确,如果不正确,响应错误,如果正确,在响应头中设置 cookie,附带登录认证信息(至于登录认证信息是设么样的,如何设计,要考虑哪些问题,就是另一个话题了,可以了解 JWT)
  3. 客户端收到 cookie,浏览器自动记录下来

后续请求

  1. 浏览器发送请求到服务器,希望添加一个管理员,并将 cookie 自动附带到请求中
  2. 服务器先获取 cookie,验证 cookie 中的信息是否正确,如果不正确,不予以操作,如果正确,完成正常的业务流程
最近更新:: 2025/7/16 12:57
Contributors: AK
Prev
常见请求方法 👌
Next
面试题