在日常开发或测试工作中,我们经常需要将 A 页面的 Cookie 拷贝到 B 页面(比如模拟用户登录状态、同步环境配置),手动复制粘贴不仅繁琐,还容易出错。既然有痛点,不如动手开发一款 Chrome 插件来解决 ------ 这不仅能自动化重复操作,还能系统掌握 Chrome 扩展开发的核心能力。
本文将分为两部分:先梳理 Chrome 插件开发的基础知识,再基于实际需求实现 "Cookie 跨页面迁移插件",最终成品支持提取当前页 Cookie、向目标页注入 Cookie、本地保存 Cookie 集三大核心功能。
基础知识
在动手前,我们需要先了解 Chrome 扩展的核心组成和工作原理。建议结合Chrome 官方开发指南学习,以下是关键知识点梳理:
模块
Chrome 插件由多个功能模块组成,不同模块负责不同职责,协同完成插件功能:
模块 | 作用 | 核心特点 |
---|---|---|
manifest.json | 插件配置 | 根目录下的配置文件,记录插件名称、版本、权限、资源路径等元数据,是插件运行的基础 |
Service Worker | 后台服务 | 无界面、在后台持续运行,处理浏览器事件(如标签切换)、管理全局状态、协调其他模块通信 |
Popup(弹窗) | 用户交互界面 | 点击插件图标弹出的窗口(由 popup.html+popup.js 构成),负责接收用户操作(如按钮点击) |
manifest.json
manifest.json 是插件的 "灵魂",所有模块和权限都需在此声明。目前 Chrome 推荐使用Manifest V3(取代旧版 V2),以下是基础配置模板(含关键注释):
json
{
"manifest_version": 3, // 必须为3,声明使用Manifest V3版本
"name": "插件名称", // 插件显示名称(如"Cookie Maker")
"version": "1.0", // 版本号(更新时需修改)
"description": "插件功能描述", // 简短说明插件用途
"icons": { // 插件图标(不同尺寸适配不同场景)
"16": "images/icon-16.png", // 扩展管理页面小图标
"32": "images/icon-32.png", // 扩展管理页面中图标
"48": "images/icon-48.png", // 扩展管理页面大图标
"128": "images/icon-128.png" // Chrome应用商店图标
},
"background": { // 后台服务配置
"service_worker": "background.js" // 指向后台脚本文件
},
"action": { // 插件图标点击行为
"default_popup": "popup.html", // 点击图标弹出的页面
"default_icon": { // 图标路径(与上方icons一致)
"16": "images/icon-16.png",
"32": "images/icon-32.png"
}
},
"permissions": [ // 插件需要的权限
"cookies", // 操作Cookie的权限
"activeTab", // 访问当前活动标签页的权限
"storage", // 本地存储数据的权限
"scripting" // 注入脚本到网页的权限
],
"host_permissions": [ // 允许操作的网站范围
"<all_urls>" // 允许操作所有网站(根据需求可缩小范围)
]
}
popup.html
定义扩展工具栏图标的「弹出界面」(点击扩展图标时显示的小窗口),是扩展与用户交互的主要前端界面之一。本质是一个 HTML 文件,可包含 CSS 样式和 DOM 结构,支持常规 HTML 元素(按钮、输入框、列表等)。
popup.js
作为 popup.html
的配套脚本,负责处理弹出界面的交互逻辑(如按钮点击、数据渲染、与后台通信等)。
- 与
popup.html
绑定,通过<script src="popup.js"></script>
引入,运行在弹出窗口的上下文环境中。 - 生命周期与
popup.html
一致:窗口打开时加载,关闭时销毁,因此变量和状态不会持久保存。 - 可直接操作
popup.html
的 DOM,也能通过 Chrome API(如chrome.runtime.sendMessage
)与background.js
或内容脚本通信,获取 / 提交数据。
service_worker-background.js
作为扩展的「后台服务」,负责处理长期运行的逻辑、监听浏览器事件、管理扩展全局状态等,是扩展的「大脑」。
- 运行在独立的后台进程中,生命周期与浏览器一致(浏览器启动时加载,关闭时终止),即使弹出窗口关闭也能持续运行。
- 无界面(不可直接操作 DOM),主要通过监听 Chrome 事件(如标签页切换、网络请求、扩展安装等)触发逻辑。
- 可存储全局数据(如用户配置、长期状态),并作为中间层协调
popup.js
、内容脚本、其他扩展组件之间的通信。
交互流程
Chrome 插件的各模块并非孤立运行,而是通过固定流程协同工作,以 "用户点击按钮→提取 Cookie" 为例:
- 用户点击插件图标 → 浏览器加载
popup.html
和popup.js
,弹出交互窗口; - 用户点击 "提取 Cookie" 按钮 →
popup.js
触发点击事件,通过chrome.runtime.sendMessage
向background.js
发送请求; background.js
监听消息,调用chrome.cookies.getAll
API 提取当前页 Cookie,将结果返回给popup.js
;popup.js
接收结果,更新弹窗 DOM(如显示 Cookie 列表),完成交互;- 用户关闭弹窗 →
popup.html
和popup.js
销毁,但background.js
仍在后台运行,等待下一次请求。
通过这种分工,popup
负责前端交互,background
负责后台逻辑和状态管理,形成了 Chrome 扩展的核心架构。
一个用于cookie跨页面提取和插入的chrome插件
了解基础知识后,我们开始开发核心功能。插件最终实现以下能力:
- 提取当前页面的所有 Cookie;
- 向目标页面注入指定 Cookie;
- 本地保存多个 Cookie 集(支持加载、删除);
- 支持 JSON / 表格两种视图查看 Cookie。
项目结构:
bash
cookie-maker/
├─ images/ # 插件图标文件夹
│ ├─ icon16.png
│ ├─ icon48.png
│ └─ icon128.png
├─ manifest.json # 核心配置文件
├─ popup.html # 弹窗界面
├─ popup.js # 弹窗交互逻辑
└─ background.js # 后台服务逻辑
1. 配置manifest.json
根据插件功能,声明所需权限和资源路径,这里我们的扩展需要以下权限:
cookies
:用于读取和修改CookieactiveTab
:用于获取当前活动标签页信息storage
:用于在本地存储保存的Cookie集scripting
:用于在页面中执行脚本<all_urls>
:允许扩展操作所有网站的Cookie
json
{
"manifest_version": 3,
"name": "Cookie Maker",
"version": "1.0",
"description": "跨标签页提取和注入Cookie的Chrome扩展",
"permissions": [
"cookies",
"activeTab",
"storage",
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
},
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
}
2. 开发弹窗界面(popup.html)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cookie Maker</title>
<style>
<!-- 省略 -->
</style>
</head>
<body>
<h1>Cookie Maker</h1>
<!-- 当前URL和Cookie操作 -->
<div class="input-group">
<label for="current-url">当前页面URL</label>
<input type="text" id="current-url" placeholder="https://example.com">
</div>
<div class="button-group">
<button id="extract-cookies">提取当前页面Cookie</button>
<button id="load-url">加载当前标签URL</button>
</div>
<!-- 目标URL输入 -->
<div class="input-group">
<label for="target-url">目标URL</label>
<input type="text" id="target-url" placeholder="https://example.com">
</div>
<!-- Cookie展示区域 -->
<div class="input-group">
<label>Cookie数据</label>
<div class="view-switcher">
<button id="json-view-btn" class="secondary active">JSON视图</button>
<button id="table-view-btn" class="secondary">表格视图</button>
</div>
<textarea id="cookies-json" placeholder="{}"></textarea>
<div id="cookies-table-container" class="table-container">
<table id="cookies-table" class="cookie-table">
<thead>
<tr>
<th><input type="checkbox" id="select-all-cookies" title="全选/取消全选" /></th>
<th>名称</th>
<th>值</th>
<th>域名</th>
<th>路径</th>
<th>过期时间</th>
<th>安全</th>
<th>HttpOnly</th>
</tr>
</thead>
<tbody id="cookies-table-body">
<!-- 表格内容将通过JavaScript动态生成 -->
</tbody>
</table>
</div>
</div>
<div class="button-group">
<button id="inject-cookies">向目标URL注入Cookie</button>
<button id="format-json" class="secondary">格式化JSON</button>
</div>
<!-- 保存和加载Cookie -->
<div class="input-group">
<label for="cookie-set-name">Cookie集名称</label>
<input type="text" id="cookie-set-name" placeholder="输入名称保存当前Cookie集">
</div>
<div class="button-group">
<button id="save-cookies">保存Cookie集</button>
<button id="load-saved-cookies" class="secondary">加载Cookie集</button>
</div>
<!-- 已保存的Cookie集 -->
<div class="saved-cookies">
<div class="saved-cookies-header">
<label>已保存的Cookie集</label>
<button id="refresh-saved-cookies" class="secondary">刷新</button>
</div>
<div id="saved-cookies-list" class="saved-cookies-list">
<!-- 已保存的Cookie集将在这里显示 -->
</div>
</div>
<!-- 消息提示区域 -->
<div id="message" class="message"></div>
<script src="popup.js"></script>
</body>
</html>
3. 开发弹窗交互逻辑(popup.js)
popup.js 负责处理用户操作(如点击按钮、视图切换),并与 background.js 通信获取 / 提交数据。
部分代码如下:
js
/**
* Chrome扩展的popup脚本
* 负责处理用户界面交互和与后台脚本的通信
*/
// 等待DOM加载完成
document.addEventListener('DOMContentLoaded', async () => {
// 绑定事件监听器
document.getElementById('extract-cookies').addEventListener('click', extractCookies);
// 。。。
// 监听JSON文本变化,自动更新表格视图和缓存
document.getElementById('cookies-json').addEventListener('input', function() {
updateTableFromJson();
cacheCookieData();
});
// 初始化页面
await loadCurrentTabUrl();
await refreshSavedCookiesList();
// 加载缓存的Cookie数据
await loadCachedCookieData();
switchView('table')
});
/**
* 缓存Cookie数据到background.js
*/
async function cacheCookieData() {
const cookiesJson = document.getElementById('cookies-json').value.trim();
try {
await chrome.runtime.sendMessage({
action: 'cacheCookieData',
cookiesJson
});
} catch (error) {
console.warn('缓存Cookie数据失败:', error);
}
}
/**
* 从background.js加载缓存的Cookie数据
*/
async function loadCachedCookieData() {
try {
const response = await chrome.runtime.sendMessage({
action: 'loadCachedCookieData'
});
if (response && response.success && response.cachedData) {
document.getElementById('cookies-json').value = response.cachedData;
updateTableFromJson();
}
} catch (error) {
console.error('加载缓存的Cookie数据失败:', error);
}
}
/**
* 从当前页面提取Cookie
*/
async function extractCookies() {
const url = document.getElementById('current-url').value.trim();
if (!url) {
showMessage('请输入URL', false);
return;
}
try {
const response = await chrome.runtime.sendMessage({
action: 'extractCookies',
url
});
if (response.success) {
document.getElementById('cookies-json').value = JSON.stringify(response.cookies, null, 2);
updateTableFromJson(); // 更新表格视图
await cacheCookieData(); // 缓存Cookie数据
showMessage(`成功提取 ${response.cookies.length} 个Cookie`);
} else {
showMessage(`提取Cookie失败: ${response.error}`, false);
}
} catch (error) {
showMessage(`提取Cookie失败: ${error.message}`, false);
}
}
/**
* 向目标URL注入Cookie
*/
async function injectCookies() {
const url = document.getElementById('target-url').value.trim();
const cookiesJson = document.getElementById('cookies-json').value.trim();
if (!url) {
showMessage('请输入目标URL', false);
return;
}
if (!cookiesJson) {
showMessage('请输入Cookie数据', false);
return;
}
try {
const cookies = JSON.parse(cookiesJson);
// 如果当前是表格视图,检查勾选状态
let cookiesToInject = cookies;
const tableViewBtn = document.getElementById('table-view-btn');
if (tableViewBtn.classList.contains('active')) {
const checkedCheckboxes = document.querySelectorAll('#cookies-table-body input[type="checkbox"]:checked');
if (checkedCheckboxes.length > 0) {
// 只注入勾选的Cookie
const checkedIndices = Array.from(checkedCheckboxes).map(checkbox =>
parseInt(checkbox.id.replace('cookie-', ''))
);
cookiesToInject = cookies.filter((_, index) => checkedIndices.includes(index));
}
}
const response = await chrome.runtime.sendMessage({
action: 'injectCookies',
url,
cookies: cookiesToInject
});
if (response.success) {
showMessage('成功注入Cookie');
} else {
showMessage(`注入Cookie失败: ${response.error}`, false);
}
} catch (error) {
showMessage(`解析Cookie失败: ${error.message}`, false);
}
}
4. 开发后台服务逻辑(background.js)
核心功能包括:
- 提取 Cookie:调用
chrome.cookies.getAll
获取指定 URL 的 Cookie; - 注入 Cookie:调用
chrome.cookies.set
向目标 URL 写入 Cookie; - 本地存储:通过
chrome.storage.local
保存 / 加载 Cookie 集; - 消息监听:接收并处理
popup.js
发送的各类请求。
部分代码如下:
js
/**
* Chrome扩展的后台脚本
* 负责处理Cookie的提取和注入逻辑
*/
// 监听来自popup的消息
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.action) {
case 'extractCookies':
extractCookies(message.url).then(cookies => {
sendResponse({ success: true, cookies });
}).catch(error => {
sendResponse({ success: false, error: error.message });
});
return true; // 保持消息通道开放,以便异步响应
case 'injectCookies':
injectCookies(message.url, message.cookies).then(() => {
sendResponse({ success: true });
}).catch(error => {
sendResponse({ success: false, error: error.message });
});
return true; // 保持消息通道开放,以便异步响应
// 。。。
});
/**
* 缓存Cookie数据到chrome.storage.local
* @param {string} cookiesJson - Cookie数据的JSON字符串
* @returns {Promise<void>}
*/
async function cacheCookieData(cookiesJson) {
if (cookiesJson) {
try {
// 验证JSON格式
JSON.parse(cookiesJson);
await chrome.storage.local.set({
cachedCookieData: {
data: cookiesJson,
timestamp: new Date().getTime()
}
});
} catch (error) {
// JSON格式不正确,不缓存
console.warn('缓存Cookie数据失败,JSON格式不正确:', error);
throw new Error('JSON格式不正确');
}
}
}
/**
* 从chrome.storage.local加载缓存的Cookie数据
* @returns {Promise<string|null>} - 缓存的Cookie数据JSON字符串或null
*/
async function loadCachedCookieData() {
try {
const result = await chrome.storage.local.get('cachedCookieData');
if (result.cachedCookieData && result.cachedCookieData.data) {
return result.cachedCookieData.data;
}
return null;
} catch (error) {
console.error('加载缓存的Cookie数据失败:', error);
throw error;
}
}
/**
* 从指定URL提取所有Cookie
* @param {string} url - 要提取Cookie的URL
* @returns {Promise<Array>} - Cookie数组
*/
async function extractCookies(url) {
try {
console.log('extractCookies', url);
const cookies = await chrome.cookies.getAll({ url });
if (cookies.length === 0) {
return 'No cookies found';
}
return cookies;
} catch (error) {
console.error('提取Cookie失败:', error);
throw error;
}
}
/**
* 向指定URL注入Cookie
* @param {string} url - 要注入Cookie的URL
* @param {Array} cookies - 要注入的Cookie数组
* @returns {Promise<void>}
*/
async function injectCookies(url, cookies) {
try {
const domain = new URL(url).hostname;
let failedCookies = [];
// 遍历所有Cookie并注入,对每个Cookie单独处理错误
for (const cookie of cookies) {
try {
// 构建Cookie参数
const cookieParams = {
url: url,
name: cookie.name,
value: cookie.value,
domain: domain,
path: cookie.path || '/',
secure: cookie.secure || false,
httpOnly: cookie.httpOnly || false,
sameSite: cookie.sameSite || 'unspecified'
};
// 如果Cookie有过期时间,添加到参数中
if (cookie.expirationDate) {
cookieParams.expirationDate = cookie.expirationDate;
}
// 单独处理每个Cookie的注入
await chrome.cookies.set(cookieParams);
} catch (error) {
// 记录失败的Cookie,但继续处理其他Cookie
console.warn(`注入Cookie ${cookie.name || '未知名称'} 失败:`, error);
failedCookies.push({
name: cookie.name || '未知名称',
error: error.message
});
}
}
// 如果有Cookie注入失败,抛出错误信息
if (failedCookies.length > 0) {
const totalSuccess = cookies.length - failedCookies.length;
const failedNames = failedCookies.map(c => c.name).join(', ');
throw new Error(`成功注入 ${totalSuccess} 个Cookie,但有 ${failedCookies.length} 个注入失败: ${failedNames}`);
}
} catch (error) {
console.error('注入Cookie过程中出现错误:', error);
throw error;
}
}
注意事项: 因为我们需要跨页面提取和注入cookie,点击另一个页面的时候popup会关闭,因此需要将数据存储在chrome.storage.local中,chrome.storage.local与插件绑定,除非主动删除否则永久存在。
5. 安装使用
5.1. 安装
- 打开Chrome浏览器,访问
chrome://extensions/
- 开启右上角的 "开发者模式"
- 点击 "加载已解压的扩展程序"
- 选择本项目的根目录
- 扩展将被添加到Chrome浏览器中
5.2. 使用
- 点击Chrome浏览器工具栏中的Cookie Maker图标打开扩展面板
- 默认会显示当前标签页的URL
- 提取Cookie:点击 "提取当前页面Cookie" 按钮,扩展会自动提取当前页面的所有Cookie
- 注入Cookie:在 "目标URL" 输入框中输入目标网站地址,确保Cookie数据已正确填写,然后点击 "向目标URL注入Cookie" 按钮
- 保存Cookie集:输入Cookie集名称,点击 "保存Cookie集" 按钮
- 加载Cookie集:输入要加载的Cookie集名称,点击 "加载Cookie集" 按钮
- 已保存的Cookie集:扩展面板下方会显示所有已保存的Cookie集,可以直接点击 "加载" 或 "使用" 按钮进行操作
代码地址:github.com/sunqqw/cook...
如果觉得有用,欢迎 Star 支持,也欢迎提交 PR 一起完善功能!