当前位置: 首页 > news >正文

【JavaScript】 HTTP Cookie 核心知识梳理与常用的封装实现

文章目录

  • 前言
  • 一、Cookie介绍
    • 1.1 Cookie基本结构
    • 1.2 Cookie的生命周期
    • 1.3 Cookie的作用域
    • 1.4 Cookie的安全性
  • 二、JS里的Cookie操作封装
    • 2.1 设置cookie(name, value, options = {})
    • 2.2 获取Cookie(name)
    • 2.3 删除Cookie(name, options = {})
    • 2.4 是否有指定Cookie(name)
    • 2.5 获取所有Cookie()
    • 2.6 清空所有Cookie(options = {})
    • 2.7 完整代码封装
  • 总结


前言

HTTP Cookie(也叫 Web Cookie 或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。也可用于保存JWT令牌,实现身份鉴权。同样也可以存储特点数据,保存到浏览器上。

cookie 大致有以下几种用法

  • 会话状态管理,如登录状态。验证令牌。
  • 个性化设置,如用户自定义设置、web主题等。

在浏览器发起的 HTTP/HTTPS 请求 中,符合条件的 Cookie 会被浏览器自动携带发送到服务器。同域名、路径匹配、协议符合条件时,浏览器会自动携带 Cookie。
这也是.NET传统开发中WebFroms 和 ASP.NET MVC实现身份鉴权的方式,浏览器发起请求默认带上身份Cookie,发送到服务器,服务器实现身份信息校验。这种方式容易导致CSRF攻击。

CSRF攻击原理

CSRF(Cross - Site Request Forgery),即跨站请求伪造,是一种黑客攻击手段。它利用用户已登录的身份,在用户毫不知情的情况下,以用户的名义执行非授权操作,像转账、修改账户信息等。
用户登录受信任网站 A,并在浏览器本地生成 Cookie:此时用户在网站 A 上处于登录状态,浏览器保存了该网站的 Cookie。恶意网站 B 会向网站 A 发送请求。由于浏览器会自动携带网站 A 的 Cookie,网站 A 无法分辨该请求是用户的真实操作还是恶意请求,从而导致攻击发生。

一、Cookie介绍

1.1 Cookie基本结构

在 JavaScript 中,Document.cookie 是一个 属性,用于操作当前网页的 Cookie。可以把Document.cookie当成一个 getter(访问器) 和 setter(修改器),用于获取BOM上的Cookie值。

cookie 是一个字符串。多条 cookie 用分号和空格 (; )分隔 ,单条cookie即key=value 键值对(建议用encodeURIComponent ,decodeURIComponent 编码解码处理)

比如

document.cookie="name=araby; gender=male";

1.2 Cookie的生命周期

Cookie 的生命周期一般分为会话期 Cookie和**持久性 Cookie **。主要取决于过期时间和有效时间这两个属性是否设置。

  • 会话期 Cookie 会在当前的会话结束之后删除。浏览器定义了“当前会话”结束的时间,一些浏览器重启时会使用会话恢复。这可能导致会话 cookie 无限延长。
  • 持久性 Cookie 在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除。
  • Expires (过期时间 - 绝对时间):告诉浏览器一个具体的日期和时间,到了这个时间点,Cookie 就应该被删除
  • Max-Age (有效期 - 相对时间):它告诉浏览器 Cookie 自被设置之日起,应该存活多少秒。浏览器在接收到 Cookie 时开始计时。【更推荐,类似于JS的new Date】
  • Max-Age优先级更高。Max-Age 和 Expires 同时设置的情况下,Max-Age生效

下面是会话期 Cookie 和持久性 Cookie 二者的比较

特性会话期 Cookie持久性 Cookie
生命周期当前的会话结束(关闭页面/浏览器)之后删除在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除或清空cookie
存储位置浏览器内存 (RAM)用户硬盘
关键属性没有 Expires 或 Max-Age (或设为无效值)必须设置有效的 Expires 或 Max-Age 属性
主要用途维护单次访问的会话状态、临时数据记住登录状态、用户偏好、长期跟踪、身份标识
优点更安全(数据不持久),隐私风险低用户体验更好(跨会话状态保持)
缺点会话结束状态丢失隐私和安全风险更高(数据持久存储)

1.3 Cookie的作用域

== Domain== 和 == Path== 标识定义了 Cookie 的作用域:即允许 Cookie 应该发送给哪些 URL。

在浏览器在发送 HTTP 请求时,会检查该请求的 URL 的主机名(Host)和路径(Path),然后查找所有满足以下条件的 Cookie 并附加到请求头 Cookie 中。

其中Domain指定了哪些主机可以接受 Cookie。通过 Domain 属性明确指定一个域,Cookie 会被发送给 指定的域名已经及其所有子域名。如果未指定,浏览器会默认将该 Cookie 的域设置为页面的域名。
比如Domain设置"example.com",则www.example.com,item.example.com也会被共享到指定Cookie 。

Path则是进一步在Domain的基础上进一步限制 Cookie 可被发送到哪些 URL 路径及其子路径。Path采用的是前缀匹配,也就是说如果设置。如果未指定,浏览器会默认将该 Cookie 的Path设置为页面的路径。
比如Path设置为"/home",则/home/homepage可以被共享到指定Cookie ,但是/user下的目录就没法访问。

1.4 Cookie的安全性

前面简单提到了CSRF攻击,了解到Cookie会在符合条件的情况下自动附加到请求头上。而Cookie 的SameSite属性则是允许服务器指定是否/何时通过跨站点请求发送,从而限制Cookie在跨站请求时的发送行为,保护数据安全。

  • Strict :最严格的模式,cookie仅发送到它来源的站点;
  • Lax:宽松模式,是跨站请求安全性和用户体验中的一种平衡。它允许部分跨站请求携带 Cookie,但仅限于安全的HTTP 方法(如 GET)和顶级导航(如链接跳转)。不支持AJAX,POST和iframe。
  • None:无限制:允许 所有跨站请求 携带 Cookie,但必须同时设置 Secure 属性,且仅在 HTTPS 连接中生效。

除了SameSite属性外,Cookie还有HttpOnly和Secure。设置 HttpOnly=true 后,JavaScript 无法访问该 Cookie(防 XSS 攻击)。设置 Secure=true 后,Cookie仅在 HTTPS 连接中传输。

模式适用场景安全建议
Strict敏感操作(如支付、删除)配合 HttpOnly 和 Secure
Lax大部分 Web 应用默认首选,平衡安全与体验
None跨域服务(如 API、第三方登录)必须配合 Secure,仅限 HTTPS

二、JS里的Cookie操作封装

下面分别介绍下JS里的Cookie操作封装。完整代码附在最后面。

2.1 设置cookie(name, value, options = {})

  1. 先判断cookie的name是否合规
if (!name || /[^\w-]/.test(name)) {throw new Error('Cookie 名称无效,只能包含字母、数字、下划线和连字符');
}

正则字符串\w-用于表示匹配字母、数字或下划线和连字符。最后[ ^ ]表示不匹配括号内的任意一个字符。

  1. 然后拼接cookie最基本的键值对,使用encodeURIComponent编码。
// 构建cookie字符串
let cookieStr = `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}`;
  1. 设置杂项,如过期时间,域,路径等属性
// 过期时间,maxAge的优先级高于expires
if (options.maxAge != null) {cookieStr += `; max-age=${Math.max(0, Math.floor(options.maxAge))}`;
} else if (options.expires != null) {const expires = options.expires instanceof Date? options.expires: new Date(Date.now() + options.expires * 1000);cookieStr += `; expires=${expires.toUTCString()}`;
}
// 处理其他选项
if (options.path) cookieStr += `; path=${options.path}`;
if (options.domain) cookieStr += `; domain=${options.domain}`;
if (options.secure) cookieStr += `; secure`;
if (options.sameSite) cookieStr += `; samesite=${options.sameSite}`;
if (options.httpOnly) cookieStr += `; httponly`;
  1. 将cookie保存到document上
document.cookie = cookieStr;

指定注意的是保存如果两个同名Cookie的路径域名完全一致,后保存的会覆盖前者。

2.2 获取Cookie(name)

获取cookie就简单多了,前文我们知道浏览器会自己更具cookie属性决定哪些页面的Http上下文需要附加cookie。这一步浏览器自身会处理,所以如果我们只需要通过JS尝试获取cookie。

通过(?:)设置非匹配组,找到字符串开头( ^ )的,或者是个分号加任意空白字符(;\s*)。
[ ^; ]*匹配零个或多个不是分号的字符。

最后再解码返回结果。

const encodedName = encodeURIComponent(name);
const cookie = document.cookie.match(new RegExp(`(?:^|;\\s*)${encodedName}=([^;]*)`)
);
return cookie ? decodeURIComponent(cookie[1]) : null;

2.3 删除Cookie(name, options = {})

JS里删除cookie时间上是通过设置过期时间这种方式来间接使cookie失效。

this.set(name, '', {...options,maxAge: -1 // 立即过期
});

2.4 是否有指定Cookie(name)

调用封装的获取cookie方法,看看是不是为null

return this.get(name) !== null;

2.5 获取所有Cookie()

先将将整个Cookie字符串按分号 (😉 拆分成数组;
使用reduce() 遍历数组,把每个Cookie转换成一个对象属性;
通过str.trim()去掉空格,使用split(‘=’)将键值对拆分。
如果解析出的 name 存在,就将这个键值对添加到定义好的数据里,最后返回。

return document.cookie.split(';').reduce((cookies, str) => {const [name, value] = str.trim().split('=').map(decodeURIComponent);return name ? { ...cookies, [name]: value } : cookies;
}, {});

2.6 清空所有Cookie(options = {})

先获取所有Cookie,调用封装的删除方法。

for (const name of Object.keys(this.getAll())) {this.delete(name, options);
}

2.7 完整代码封装

class EFCookieUtil {/*** 设置 Cookie* @param {string} name - Cookie 名称* @param {string|number|boolean} value - Cookie 值* @param {Object} [options] - 可选配置项* @param {number|Date} [options.expires] - 过期时间(秒或日期对象)* @param {number} [options.maxAge] - 最大存活时间(秒,优先级高于 expires)* @param {string} [options.path] - 路径* @param {string} [options.domain] - 域名* @param {boolean} [options.secure] - 是否只在 HTTPS 发送* @param {'Strict'|'Lax'|'None'} [options.sameSite] - 同站策略* @param {boolean} [options.httpOnly=false] - 是否禁止 JavaScript 访问*/static set(name, value, options = {}) {if (!name || /[^\w-]/.test(name)) {throw new Error('Cookie 名称无效,只能包含字母、数字、下划线和连字符');}// 构建 cookie 字符串let cookieStr = `${encodeURIComponent(name)}=${encodeURIComponent(String(value))}`;// 优先级:maxAge > expiresif (options.maxAge != null) {cookieStr += `; max-age=${Math.max(0, Math.floor(options.maxAge))}`;} else if (options.expires != null) {const expires = options.expires instanceof Date? options.expires: new Date(Date.now() + options.expires * 1000);cookieStr += `; expires=${expires.toUTCString()}`;}// 处理其他选项if (options.path) cookieStr += `; path=${options.path}`;if (options.domain) cookieStr += `; domain=${options.domain}`;if (options.secure) cookieStr += `; secure`;if (options.sameSite) cookieStr += `; samesite=${options.sameSite}`;if (options.httpOnly) cookieStr += `; httponly`;document.cookie = cookieStr;}/*** 获取 Cookie 值* @param {string} name - Cookie 名称* @returns {string|null} - Cookie 值或 null*/static get(name) {if (!name) return null;const encodedName = encodeURIComponent(name);const cookie = document.cookie.match(new RegExp(`(?:^|;\\s*)${encodedName}=([^;]*)`));return cookie ? decodeURIComponent(cookie[1]) : null;}/*** 删除 Cookie* @param {string} name - Cookie 名称* @param {Object} [options] - 需与设置时一致* @param {string} [options.path] - 路径* @param {string} [options.domain] - 域名*/static delete(name, options = {}) {this.set(name, '', {...options,maxAge: -1 // 立即过期});}/*** 检查 Cookie 是否存在* @param {string} name - Cookie 名称* @returns {boolean} - 是否存在*/static has(name) {return this.get(name) !== null;}/*** 获取所有 Cookie* @returns {Object} - 包含所有 Cookie 的对象*/static getAll() {return document.cookie.split(';').reduce((cookies, str) => {const [name, value] = str.trim().split('=').map(decodeURIComponent);return name ? { ...cookies, [name]: value } : cookies;}, {});}/*** 清除所有 Cookie(需指定作用域)* @param {Object} [options] - 作用域配置* @param {string} [options.path] - 路径* @param {string} [options.domain] - 域名*/static clear(options = {}) {for (const name of Object.keys(this.getAll())) {this.delete(name, options);}}
}

总结

本文分析了Cookie 的键值对结构、会话期 / 持久性区别,以及 Domain/Path 如何控制作用域。并讨论到SameSite、HttpOnly 与 Secure如何提升Cookie的安全性。最后基于原生 JS 封装 Cookie 工具类,涵盖增删改查、批量操作及参数配置。

相关文章:

  • 学校招生小程序源码介绍
  • c++中类的继承
  • 0610_特性和反射_加密和解密_单例模式
  • Playwright 与 Selenium:自动化测试的两大主流工具对比
  • Kubernetes 从入门到精通-pod基础管理
  • 饿一饿对肝脏好
  • ETL中图表统计分析模版组件使用
  • Java设计模式基础问答
  • 设计模式和设计原则回顾
  • C#设计模式
  • QMC5883L的驱动
  • 深入解析 GitHub Token 与 NPM Token:自动化发布的完整指南
  • pnpm install 和 npm install 的区别
  • Java如何权衡是使用无序的数组还是有序的数组
  • oracle 安全基线配置
  • MySQL主从复制实现指南
  • [2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
  • 2025蓝奏云软件库合集分享链接汇总:极刻云搜 - 一站式获取海量资源
  • 人脸识别技术应用备案材料揭秘
  • 24-Oracle 23 ai ​Lock-Free Reservations​(无锁列值保留)
  • 网站优化成都哪里好/苏州关键词搜索排名
  • 网站广告推广怎么做/seo综合查询网站源码
  • 带后台的网站模板下载/热搜榜上2023年热搜
  • 什么源码做有趣的网站/宁阳网站seo推广
  • 如何做生鲜配送网站生意/seo黑帽教学网
  • 做班级网站的素材/电子商务推广