开源分享:实习中开发的单点登录Cookie管理神器 | 从痛点到产品的完整实践

CookieFlow

🚀 一个简洁现代的Cookie复制和管理工具

📖 开发背景

单点登录导致不同环境需要使用不同个cookie,手动去控制台粘贴过于繁琐麻烦,于是开发这款插件用于提高调试效率。


🛠️ 使用技术栈


✨ 插件功能

  • ✅ 复制源地址cookies到目标地址
  • ✅ 清空目标地址的cookies
  • ✅ 历史记录支持且可删除
  • URL合法性校验
  • ✅ 消息提示弹窗
  • ✅ 自动读取当前页面URL

🏗️ 项目架构

参考百度TDA平台中AI助手组件的架构设计:UI层与业务逻辑抽离,状态管理与工具函数抽离,采用高内聚低耦合、单一职责原则的分层架构。

scss 复制代码
🎨 UI层 (Popup.jsx) ← 纯视图渲染
    ↓
⚛️ Hook层 (useCookieFlow.js) ← 状态管理 + 副作用处理
    ↓
🔧 Utils层 (cookie.js, storage.js, urlValidation.js) ← 纯函数工具集

📁 代码结构

csharp 复制代码
CookieFlow/
├── 📁 assets/
│   ├── 📄 icon.svg              # 插件图标
│   └── 📄 icon*.png             # 不同尺寸图标
├── 📁 src/
│   ├── 📁 components/
│   │   └── 📄 UrlInput.jsx      # URL输入框组件
│   ├── 📁 hooks/
│   │   └── 📄 useCookieFlow.js  # 自定义业务Hook
│   ├── 📁 pages/
│   │   └── 📁 popup/
│   │       ├── 📄 index.html    # 弹窗页面模板
│   │       ├── 📄 index.jsx     # React应用入口
│   │       └── 📄 Popup.jsx     # 主弹窗组件
│   ├── 📁 styles/
│   │   └── 📄 index.css         # 全局样式
│   └── 📁 utils/                # 工具函数层
│       ├── 📄 cookie.js         # Cookie操作核心逻辑
│       ├── 📄 storage.js        # Chrome存储API封装
│       ├── 📄 history.js        # 历史记录处理逻辑
│       ├── 📄 message.jsx       # 消息提示封装
│       ├── 📄 urlValidation.js  # URL校验工具
│       └── 📄 init.js           # 初始化逻辑
├── 📄 manifest.json             # Chrome扩展配置
├── 📄 package.json              # 项目依赖配置
└── 📄 vite.config.js            # Vite构建配置

🚀 快速开始

开发命令

bash 复制代码
# 安装依赖
npm install

# 开发模式
npm run dev

# 构建生产版本
npm run build

安装使用

  1. 克隆项目代码
  2. 运行 npm install 安装依赖
  3. 运行 npm run build 构建项目
  4. 在Chrome中打开扩展程序页面 (chrome://extensions/)
  5. 启用"开发者模式"
  6. 点击"加载已解压的扩展程序",选择项目的 dist 目录

🔌 Chrome API 使用

本项目主要使用以下Chrome扩展API:

  • chrome.cookies - 管理浏览器Cookie数据

    • getAll() - 获取指定URL的Cookie
    • set() - 设置Cookie
    • remove() - 删除Cookie
  • chrome.tabs - 获取浏览器标签页信息

    • query() - 查询当前活跃标签页
  • chrome.storage.local - 本地数据存储

    • get() - 读取本地存储
    • set() - 保存本地存储

🔗 开源地址

GitHub : Mebius1916/CookieFlow

本项目采用开源协议,欢迎提交 PR 参与开源贡献!🎉


📚 源码讲解

1️⃣ Cookie复制功能

功能流程: 输入验证 → 获取源Cookie → 设置目标Cookie → 保存历史记录 → 显示结果

1.1 顶层处理函数

位于 useCookieFlow.js 中的主处理函数,负责状态管理和流程控制:

javascript 复制代码
const handleCopyCookies = async () => {
  setCopyLoading(true);  // 设置加载状态
  
  try {
    const result = await copyCookies(sourceUrl, targetUrl);
    if (result) {
      // 更新历史记录UI
      await updateHistory(setSourceHistory, setTargetHistory);
    }
  } finally {
    setCopyLoading(false);  // 恢复按钮状态
  }
};
1.2 核心业务逻辑

位于 cookie.js 中的 copyCookies 函数,实现完整的复制流程:

javascript 复制代码
export const copyCookies = async (sourceUrl, targetUrl) => {
  // 1. URL预处理:提取origin部分(协议+域名+端口)
  const processedSourceUrl = extractOrigin(sourceUrl);
  const processedTargetUrl = extractOrigin(targetUrl);
  
  // 2. URL合法性验证
  const { validateFormUrls } = await import('./urlValidation');
  const validation = validateFormUrls(processedSourceUrl, processedTargetUrl);
  
  if (!validation.isValid) {
    const { message } = await import('antd');
    message.error(validation.errors[0]);
    return false;
  }
  
  try {
    // 3. 获取源地址所有Cookie
    const cookies = await getCookies(processedSourceUrl);
    
    // 4. 检查Cookie数量
    if (cookies.length === 0) {
      const { showInfo } = await import('./message');
      showInfo('源地址没有可复制的Cookie');
      return true;
    }
    
    // 5. 批量设置目标地址Cookie
    const result = await setCookies(cookies, processedTargetUrl);
    
    // 6. 保存操作记录到历史
    await saveCookieOperation(processedSourceUrl, processedTargetUrl, result.successCount);
    
    // 7. 显示操作结果
    const { showOperationResult } = await import('./message');
    showOperationResult('复制', result);
    
    return true;
  } catch (error) {
    console.error('复制Cookie失败:', error);
    const { showError } = await import('./message');
    showError('复制Cookie');
    return false;
  }
};
1.3 Cookie获取与设置

获取Cookie:使用Chrome API获取指定域名下的所有Cookie

javascript 复制代码
export const getCookies = async (url) => {
  if (!isValidUrl(url)) {
    console.error('无效的URL格式:', url);
    return [];
  }
  
  try {
    return await chrome.cookies.getAll({ url });
  } catch (error) {
    console.error('获取Cookie失败:', error);
    return [];
  }
};

设置Cookie:批量设置Cookie到目标URL,保持原有属性

javascript 复制代码
export const setCookies = async (cookies, targetUrl) => {
  if (!isValidUrl(targetUrl)) {
    return { successCount: 0 };
  }
  
  let successCount = 0;
  
  for (const cookie of cookies) {
    try {
      await chrome.cookies.set({
        url: targetUrl,
        name: cookie.name,
        value: cookie.value,
        domain: cookie.domain,
        path: cookie.path,
        secure: cookie.secure,
        httpOnly: cookie.httpOnly,
        sameSite: cookie.sameSite,
        expirationDate: cookie.expirationDate,
      });
      successCount++;
    } catch (error) {
      console.error(`设置Cookie失败: ${cookie.name}`, error);
    }
  }
  
  return { successCount };
};

2️⃣ Cookie清空功能

功能流程: URL验证 → 获取所有Cookie → 逐个删除 → 显示结果

2.1 清空处理函数
javascript 复制代码
const handleClearCookies = async () => {
  setClearLoading(true);
  
  try {
    await clearCookies(targetUrl);
  } finally {
    setClearLoading(false);
  }
};
2.2 清空实现逻辑
javascript 复制代码
export const clearCookies = async (url) => {
  if (!url) {
    const { message } = await import('antd');
    message.error('目标地址不能为空');
    return { successCount: 0 };
  }

  // 提取URL的origin部分
  const processedUrl = extractOrigin(url);

  if (!isValidUrl(processedUrl)) {
    const { message } = await import('antd');
    message.error('无效的URL格式');
    return { successCount: 0 };
  }
  
  try {
    // 获取所有Cookie
    const cookies = await chrome.cookies.getAll({ url: processedUrl });
    let successCount = 0;
    
    // 逐个删除Cookie
    for (const cookie of cookies) {
      try {
        await chrome.cookies.remove({ 
          url: processedUrl, 
          name: cookie.name 
        });
        successCount++;
      } catch (error) {
        console.error(`删除Cookie失败: ${cookie.name}`, error);
      }
    }

    // 显示操作结果
    const { showOperationResult } = await import('./message');
    showOperationResult('清除', { successCount });

    return { successCount };
  } catch (error) {
    console.error('清除Cookie失败:', error);
    const { showError } = await import('./message');
    showError('清除Cookie');
    return { successCount: 0 };
  }
};

3️⃣ 历史记录管理

3.1 数据结构设计

历史记录采用数组结构,每条记录包含:

javascript 复制代码
const record = {
  source: 'https://example.com',      // 源地址
  target: 'http://localhost:8080',   // 目标地址  
  timestamp: Date.now(),             // 操作时间戳
  count: 5                           // 成功复制的Cookie数量
};
3.2 存储机制

保存操作记录:每次复制操作后自动保存

javascript 复制代码
export const saveCookieOperation = async (source, target, successCount) => {
  try {
    const record = {
      source,
      target, 
      timestamp: Date.now(),
      count: successCount
    };
    
    // 获取现有历史记录
    const history = await getCookieHistory();
    
    // 新记录插入首位,限制最多10条
    const newHistory = [record, ...(history || [])].slice(0, 10);
    
    // 保存到Chrome本地存储
    await saveCookieHistory(newHistory);
    
    return true;
  } catch (error) {
    console.error('保存Cookie操作记录失败:', error);
    return false;
  }
};

存储API封装 (位于 storage.js):

javascript 复制代码
// 读取历史记录
export const getCookieHistory = async () => {
  try {
    const result = await chrome.storage.local.get('cookieHistory');
    return result.cookieHistory || [];
  } catch (error) {
    console.error('获取Cookie历史记录失败:', error);
    return [];
  }
};

// 保存历史记录
export const saveCookieHistory = async (history) => {
  try {
    await chrome.storage.local.set({ cookieHistory: history });
  } catch (error) {
    console.error('保存Cookie历史记录失败:', error);
    throw error;
  }
};
3.3 历史记录提取

从完整历史记录中提取URL列表,用于下拉框显示:

javascript 复制代码
// 通用历史记录获取函数
const getUrlHistory = async (field) => {
  try {
    const history = await getCookieHistory();
    if (!history || history.length === 0) return [];
    
    // 使用Set去重,slice限制最多10条
    return [...new Set(history.map(item => item[field]))].slice(0, 10);
  } catch (error) {
    console.error(`获取${field}历史记录失败:`, error);
    return [];
  }
};

// 导出特定类型的历史记录获取函数
export const getSourceUrlHistory = () => getUrlHistory('source');
export const getTargetUrlHistory = () => getUrlHistory('target');
3.4 并发更新优化

使用 Promise.all 并发请求,提升性能:

javascript 复制代码
export const updateHistory = async (setSourceHistory, setTargetHistory) => {
  try {
    // 并发获取源地址和目标地址历史记录
    const [sourceHistory, targetHistory] = await Promise.all([
      getSourceUrlHistory(),
      getTargetUrlHistory()
    ]);
    
    // 更新UI状态
    setSourceHistory(sourceHistory || []);
    setTargetHistory(targetHistory || []);
  } catch (error) {
    console.error('更新历史记录失败:', error);
  }
};

4️⃣ 历史记录删除

4.1 UI交互设计

UrlInput.jsx 组件中,鼠标悬停时显示删除按钮:

javascript 复制代码
<div className="flex items-center justify-between max-w-64 cursor-pointer py-1 px-2 hover:bg-gray-100 group">
  <span className="truncate flex-1 mr-2">{url}</span>
  <DeleteOutlined
    className="text-gray-400 hover:text-red-500 opacity-0 group-hover:opacity-100 transition-opacity"
    onClick={(e) => handleDeleteHistory(index, e)}
  />
</div>
4.2 删除事件处理
javascript 复制代码
const handleDeleteHistory = (index, e) => {
  e.stopPropagation();  // 阻止事件冒泡,避免触发选择操作
  if (onDeleteHistory) {
    onDeleteHistory(index);
  }
};
4.3 删除业务逻辑

Hook层处理

javascript 复制代码
const handleDeleteSourceHistory = async (index) => {
  try {
    await deleteSourceUrlHistory(index);
    // 删除后重新获取最新历史记录
    const newSourceHistory = await getSourceUrlHistory();
    setSourceHistory(newSourceHistory || []);
  } catch (error) {
    console.error('删除源地址历史记录失败:', error);
    showError('删除历史记录');
  }
};

Utils层实现

javascript 复制代码
// 通用删除函数
const deleteUrlHistory = async (index, field) => {
  try {
    const history = await getCookieHistory();
    if (!history || history.length === 0) return;
    
    // 获取去重后的URL列表
    const urls = [...new Set(history.map(item => item[field]))].slice(0, 10);
    const urlToDelete = urls[index];
    
    if (urlToDelete) {
      // 过滤掉包含该URL的所有记录
      const newHistory = history.filter(item => item[field] !== urlToDelete);
      await saveCookieHistory(newHistory);
    }
  } catch (error) {
    console.error(`删除${field}历史记录失败:`, error);
    throw error;
  }
};

// 导出特定类型的删除函数
export const deleteSourceUrlHistory = (index) => 
  deleteUrlHistory(index, 'source');

export const deleteTargetUrlHistory = (index) => 
  deleteUrlHistory(index, 'target');

5️⃣ URL校验机制

5.1 基础校验函数
javascript 复制代码
export const isValidUrl = (url) => {
  try {
    new URL(url);  // 使用浏览器原生URL构造函数校验
    return true;
  } catch (error) {
    return false;
  }
};
5.2 表单验证

提供详细的错误信息用于用户提示:

javascript 复制代码
export const validateFormUrls = (sourceUrl, targetUrl) => {
  const errors = [];
  
  if (!sourceUrl || !sourceUrl.trim()) {
    errors.push('源地址不能为空');
  }
  
  if (!targetUrl || !targetUrl.trim()) {
    errors.push('目标地址不能为空');
  }
  
  if (sourceUrl && sourceUrl.trim() && !isValidUrl(sourceUrl.trim())) {
    errors.push('源地址格式不正确');
  }
  
  if (targetUrl && targetUrl.trim() && !isValidUrl(targetUrl.trim())) {
    errors.push('目标地址格式不正确');
  }
  
  return {
    isValid: errors.length === 0,
    errors
  };
};
5.3 URL处理

提取URL的origin部分,确保Cookie操作的准确性:

javascript 复制代码
export const extractOrigin = (url) => {
  try {
    const urlObj = new URL(url);
    return urlObj.origin;  // 返回 "协议://域名:端口"
  } catch (error) {
    console.error('URL格式无效:', url);
    return url;
  }
};

6️⃣ 消息提示系统

位于 message.jsx 中,封装Antd的message组件:

javascript 复制代码
// 操作结果提示
export const showOperationResult = (operation, result) => {
  if (!result) {
    message.error(`${operation}Cookie失败`);
    return;
  }
  
  if (result.successCount > 0) {
    message.success(`成功${operation} ${result.successCount} 个Cookie`);
  } else {
    message.info(`没有Cookie被${operation}`);
  }
};

// 错误提示
export const showError = (action) => {
  message.error(`${action}失败`);
};

// 信息提示  
export const showInfo = (content) => {
  message.info(content);
};
相关推荐
Calm5506 分钟前
ele表单未输入值提示为英文
前端
爪洼守门员21 分钟前
前端性能优化
开发语言·前端·javascript·笔记·性能优化
TOYOAUTOMATON26 分钟前
GTH系列模组介绍
前端·目标检测·自动化
测试人社区—小叶子28 分钟前
测试开发面试高频“灵魂八问”深度解析与应答策略
网络·人工智能·测试工具·云原生·容器·面试·职场和发展
2022.11.7始学前端31 分钟前
n8n第十节 把Markdown格式的会议纪要发到企微
前端·chrome·n8n
fruge1 小时前
Lodash 源码精读:防抖节流的实现细节与边界场景
前端
yuzhiboyouye1 小时前
怎么熟悉一个web前端项目的业务呢?
前端
GISer_Jing1 小时前
AI在前端营销和用户增长领域应用(待补充)
前端·人工智能
代码AI弗森1 小时前
意图识别面试通关指南:从基础问答到场景落地
面试·职场和发展
橘子海全栈攻城狮1 小时前
【最新源码】基于springboot的会议室预订系统设计与实现 014
java·开发语言·前端·spring boot·后端·spring·自动化