Skip to content

Cookie 与 Web Storage

一、Cookie 核心机制解析

1.1 诞生背景与核心特性

  • 无状态协议补偿:为解决 HTTP 无状态特性设计,实现会话跟踪
  • 客户端存储:浏览器自动管理存储/传输,最大容量 4KB
  • 同源策略
    • 域名绑定:www.baidu.comaa.baidu.com 默认隔离
    • 端口隔离:80 端口与 8080 端口 Cookie 不共享

1.2 属性详解表

属性说明最佳实践
Domain定义有效域名(默认当前域名).baidu.com 实现多级子域共享
Path定义有效路径(默认 //api 限制 API 接口专用 Cookie
Expires绝对过期时间(GMT 格式)长期登录凭证存储
Max-Age相对有效期(秒),优先级高于 Expires临时会话管理(如验证码)
Secure仅 HTTPS 传输敏感信息必须设置
HttpOnly禁止 JavaScript 访问防御 XSS 攻击
SameSite控制跨站请求携带(Strict/Lax/None)防御 CSRF 攻击(需配合 Secure)

1.3 高级应用场景

javascript
// 设置安全 Cookie 示例
document.cookie =
  "session_id=abc123; Domain=.baidu.com; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=3600";

// 读取 Cookie(需自行解析)
function getCookie(name) {
  return document.cookie.split("; ").reduce((res, c) => {
    const [key, val] = c.split("=").map(decodeURIComponent);
    return key === name ? val : res;
  }, "");
}

二、Web Storage 进阶指南

2.1 存储特性对比

特性localStoragesessionStorageCookie
存储容量5MB5MB4KB
生命周期持久化标签页关闭可配置
作用域同源不限窗口同源+同窗口同源+域名/路径限制
API 类型同步同步同步/异步(Document-cookie)
事件通知storage 事件

2.2 实用代码模板

javascript
// localStorage 持久化存储
const USER_SETTINGS_KEY = "user_prefs";

// 保存设置
function saveSettings(settings) {
  localStorage.setItem(
    USER_SETTINGS_KEY,
    JSON.stringify({
      ...settings,
      timestamp: Date.now(),
    })
  );
}

// 读取设置
function loadSettings() {
  const raw = localStorage.getItem(USER_SETTINGS_KEY);
  return raw ? JSON.parse(raw) : {};
}

// sessionStorage 临时存储
function saveTempData(key, data) {
  sessionStorage.setItem(key, JSON.stringify(data));
}

// 跨窗口通信(通过 storage 事件)
window.addEventListener("storage", (e) => {
  if (e.key === "shared_data") {
    console.log("收到其他窗口更新:", JSON.parse(e.newValue));
  }
});
js
// 打开一个新tab标签,加上opener可以复制sessionStorage,修改不会共享
<a href="./other.html" target="_blank" rel="opener">
  打开新页面
</a>

三、安全防护最佳实践

  1. 敏感数据保护
    • 避免存储明文密码等敏感信息
    • 使用 HttpOnly 阻止 XSS 窃取
  2. 传输安全
    • 强制 Secure 属性(HTTPS 环境)
    • 设置 SameSite=Strict 防御 CSRF
  3. 生命周期管理
    • 短期数据使用 Max-Age
    • 长期数据配合刷新令牌机制

3.2 Web Storage 安全风险

  • XSS 攻击:恶意脚本可读写全部数据

  • 防御方案

    javascript
    // 输入过滤
    function sanitizeInput(input) {
      const div = document.createElement("div");
      div.textContent = input;
      return div.innerHTML;
    }
    
    // 存储加密
    function saveSecureData(key, data) {
      const encrypted = CryptoJS.AES.encrypt(
        JSON.stringify(data),
        SECRET_KEY
      ).toString();
      localStorage.setItem(key, encrypted);
    }

四、性能优化策略

  • 减少体积:避免存储大型数据(超过 4KB 会被截断)

  • 作用域精准化

    javascript
    // 错误示范:设置全局 Path
    document.cookie = "temp_data=xxx; Path=/";
    
    // 正确做法:限制作用域
    document.cookie = "temp_data=xxx; Path=/api";
  • 过期策略:及时清理过期 Cookie(可通过 Max-Age 自动管理)

4.2 Web Storage 优化方案

  • 定期清理:实现存储空间监控
javascript
// 计算空间占用
const getAccurateStorageUsage = () => {
  let totalBytes = 0;
  const encoder = new TextEncoder();

  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    const value = localStorage.getItem(key) || "";

    totalBytes += encoder.encode(key).byteLength;
    totalBytes += encoder.encode(value).byteLength;
  }

  return totalBytes;
};

// 智能清理策略
const cleanupStorage = (prefix, keepCount = 10) => {
  const allKeys = [];
  const encoder = new TextEncoder();
  // 收集符合前缀的键并解析元数据
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    if (key.startsWith(prefix)) {
      allKeys.push({
        key,
        size:
          encoder.encode(key).byteLength +
          encoder.encode(localStorage.getItem(key) || "").byteLength,
      });
    }
  }

  allKeys.slice(0, -keepCount).forEach((item) => {
    console.log(`[清理] ${item.key} (${item.size}B)`);
    localStorage.removeItem(item.key);
  });
};

// 空间监控与自动清理
const checkStorageSpace = (
  options = {
    maxWarning: 4.5 * 1024 * 1024, // 4.5MB 预警阈值
    cleanupPrefix: "app:", // 默认清理前缀
    keepCount: 10, // 默认保留条目数
  }
) => {
  const used = getAccurateStorageUsage();
  const mbUsed = (used / 1024 / 1024).toFixed(2);

  console.log(`💾 当前存储占用: ${mbUsed}MB`);

  if (used > options.maxWarning) {
    console.warn(`⚠️ 存储空间超过 ${mbUsed}MB,触发自动清理`);
    cleanupStorage(options.cleanupPrefix, options.keepCount);
  }
};

const setStorage = (key, data={}) => {
  data = JSON.stringify(data) || "";
  const encoder = new TextEncoder();
  const size = encoder.encode(key).byteLength +
    encoder.encode(data).byteLength,
    localStorage.setItem(key, data);

  console.log(`✅ 添加数据: ${key} (${size}B)`);
};

const getStorage = (key) => {
   return JSON.parse(localStorage.getItem(key) || "{}");
};

window.addEventListener("storage", (e) => {
  if (e.storageArea === localStorage) {
    console.log(`🔔 检测到存储变化: ${e.key} = ${e.newValue}`);
    checkStorageSpace(); // 可选:自动重新检查
  }
});

export {
  getAccurateStorageUsage,
  cleanupStorage,
  checkStorageSpace,
  setStorage,
};

五、选型决策树

mermaid
graph TD
A[需要存储数据] --> B{数据类型}
B -->|敏感信息| C[Cookie + Secure/HttpOnly]
B -->|非敏感信息| D{生命周期}
D -->|持久化| E[localStorage]
D -->|临时| F{作用域需求}
F -->|跨窗口| E
F -->|单窗口| G[sessionStorage]
C --> H{数据量}
H -->|≤4KB| C
H -->|>4KB| I[需改用 Web Storage]

六、常见问题解决方案

javascript
// 主域设置
document.cookie = "shared_cookie=123; Domain=.example.com; Path=/";

// 子域读取(如 a.example.com)
console.log(document.cookie.includes("shared_cookie")); // true