稀土掘金技术社区

上班摸鱼看掘金,老板突然出现在身后...

Image
(💰TRAE SOLO实战赛强势上线,速戳上图了解详情🔍)

那是一个平静的下午。我正在掘金上摸鱼写文章,代码编辑器在另一个屏幕上装模作样地躺着。 突然,一个熟悉的声音从身后传来:

"小明,在干嘛呢?"

心跳瞬间飙到 180,肾上腺素狂飙。我的手指以肌肉记忆般的速度按下了 Alt+Q。

0.1 秒后——

屏幕上的掘金文章瞬间蒸发,取而代之的是一个朴实无华的百度首页。

我淡定转身,面不改色: "老板,正准备查个技术问题!"

老板满意地点点头,拍了拍我的肩膀:"不错,要多钻研。" 然后转身离开。

我长舒一口气,心率逐渐平复。再次按下 Alt+Q——掘金文章完美归位。

这不是科幻片,也不是魔术。

这是我上周刚撸出来的 「浏览器伪装插件」 —— 一个真正的 "职场求生神器"。

今天,我就把这个插件制作过程,毫无保留地分享给你。

先看效果

按下 Alt+Q显示百度界面, 再次按下 Alt+Q回到原来页面

需求分析

核心功能

  • 「一键切换」:按快捷键(Alt+Q)0.1 秒瞬间切换
  • 「完美伪装」:标题、图标、页面内容三位一体
  • 「无缝恢复」:再按一次,一切如初
  • 「兼容性强」:支持 99% 的网站(包括掘金、知乎、B 站...)

立即使用

「5 分钟搞定安装」:复制代码 → 创建文件 → 加载扩展 → 开始使用!

📦 获取完整代码 👉GitHub - Teernage/disguise-extension如果对你有帮助,欢迎 Star ⭐ 支持!

实现过程

第一步:创建插件结构

disguise-extension/├── manifest.json      # 插件配置文件├── background.js      # 后台脚本├── disguise.js        # 核心逻辑└── rules.json         # 网络请求规则

第二步:配置 manifest.json

{  "manifest_version": 3,  "name": "网站伪装器",  "version": "1.0",  "permissions": ["activeTab", "scripting", "declarativeNetRequest"],  "host_permissions": ["<all_urls>"],  "background": {    "service_worker": "background.js"  },  "content_scripts": [    {      "matches": ["<all_urls>"],      "js": ["disguise.js"]    }  ],  "declarative_net_request": {    "rule_resources": [      {        "id": "remove_frame_headers",        "enabled": true,        "path": "rules.json"      }    ]  },  "commands": {    "toggle": {      "suggested_key": { "default": "Alt+Q" },      "description": "切换伪装"    }  }}

「关键配置说明:」

  • permissions:申请必要权限
  • content_scripts:在所有网页注入脚本
  • declarativeNetRequest:修改 HTTP 响应头
  • commands:注册快捷键

第三步:处理快捷键(background.js)

// 监听快捷键chrome.commands.onCommand.addListener(async (command) => {  if(command === 'toggle') {    const [tab] = await chrome.tabs.query({      active: true,      currentWindow: true,    });    chrome.tabs.sendMessage(tab.id, { action: 'toggle' });  }});

「工作流程:」

用户按 Alt+Q    ↓background.js 监听到快捷键    ↓发送消息到当前标签页    ↓disguise.js 接收消息并执行切换

💡 深入理解脚本通信机制

看到这里,你可能好奇:「background.js 和 disguise.js 之间是如何通信的?为什么能跨页面传递消息?」 如果你想深入了解 它们之间真实的运行上下文与消息链路,强烈推荐阅读:

👉 大部分人都错了!这才是 Chrome 插件多脚本通信的正确姿势[2] 这篇文章用原理 + 示例拆解了常见误区与正确做法,能帮你更透彻地理解本插件的通信机制。

第四步:核心伪装逻辑(disguise.js)

const config = {  url: 'https://www.baidu.com/',  title: '百度一下',};
let isDisguised = false;let originalTitle = document.title;let originalIcon = null;
// 接收快捷键消息chrome.runtime.onMessage.addListener((msg) => {  if (msg.action === 'toggle') {    toggle();  }});
function toggle() {  isDisguised = !isDisguised;  let iframe = document.getElementById('work-iframe');
  if (isDisguised) {    // 保存原始图标    const iconLink = document.querySelector('link[rel*="icon"]');    if (iconLink) originalIcon = iconLink.href;
    // 修改标题和图标    document.title = config.title;    changeFavicon(`${config.url}/favicon.ico`);
    // 创建或显示 iframe    if (!iframe) {      iframe = document.createElement('iframe');      iframe.id = 'work-iframe';      iframe.src = config.url;
      // 设置iframe样式      Object.assign(iframe.style, {        position: 'fixed',        top: '0',        left: '0',        width: '100vw',        height: '100vh',        zIndex: '2147483647',        border: 'none',        background: 'white',      });
      // 添加加载错误处理      iframe.onerror = () => {        console.warn('iframe 加载失败,可能被跨域阻止');        showFallbackMessage(iframe);      };
      document.body.appendChild(iframe);    }    iframe.style.display = 'block';  } else if (iframe) {    // 恢复原始标题和图标    document.title = originalTitle;    if (originalIcon) {      changeFavicon(originalIcon);    } else {      // 如果没有原始图标,移除当前图标      const currentIcon = document.querySelector('link[rel*="icon"]');      if (currentIcon) currentIcon.remove();    }
    iframe.style.display = 'none';  }}
function changeFavicon(iconUrl) {  // 移除所有现有的图标链接  const existingIcons = document.querySelectorAll('link[rel*="icon"]');  existingIcons.forEach((icon) => icon.remove());
  // 创建新的图标链接  const link = document.createElement('link');  link.rel = 'icon';  link.type = 'image/x-icon';  link.href = iconUrl;  document.head.appendChild(link);}
// 当 iframe 被阻止时显示 404 页面function showFallbackMessage(iframe) {  const fallback = document.createElement('div');  fallback.id = 'fallback-message';  fallback.innerHTML = `    <div style="      display: flex;      flex-direction: column;      align-items: center;      justify-content: center;      height: 100vh;      background: #f5f5f5;      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;      margin: 0;      color: #333;    ">      <div style="text-align: center; max-width: 600px; padding: 40px;">        <h1 style="          font-size: 120px;          margin: 0;          color: #ddd;          font-weight: 700;        ">404</h1>
        <h2 style="          font-size: 24px;          margin: 20px 0;          color: #666;          font-weight: 400;        ">页面未找到</h2>
        <p style="          font-size: 16px;          color: #999;          line-height: 1.6;          margin: 20px 0;        ">          抱歉,您访问的页面不存在或已被删除。        </p>
        <div style="margin-top: 30px;">          <a href="${config.url}" style="            display: inline-block;            padding: 12px 30px;            background: #1a73e8;            color: white;            text-decoration: none;            border-radius: 4px;            font-size: 14px;            transition: background 0.3s;          " onmouseover="this.style.background='#1557b0'"              onmouseout="this.style.background='#1a73e8'">            返回首页          </a>        </div>      </div>    </div>  `;
  iframe.style.display = 'none';  document.body.appendChild(fallback);}

第五步 rules.json

很多网站(如百度)设置了 X-Frame-Options 响应头,禁止被 iframe 嵌入。

我们需要通过 declarativeNetRequest 移除这些限制:

[  {    "id": 1,    "priority": 1,    "action": {      "type": "modifyHeaders",      "responseHeaders": [        {          "header": "X-Frame-Options",          "operation": "remove"        },        {          "header": "Content-Security-Policy",          "operation": "remove"        }      ]    },    "condition": {      "urlFilter": "*",      "resourceTypes": ["sub_frame"]    }  }]

「作用:」

  • 移除 X-Frame-Options 响应头
  • 移除 Content-Security-Policy 响应头
  • 允许任何网站被 iframe 嵌入

技术细节

1. 为什么用 iframe?

「方案对比:」

方案
优点
缺点
「修改 DOM」
简单
无法完美还原复杂网站
「截图替换」
真实
无法交互
「iframe 嵌入」
完美伪装 + 可交互
可能被跨域阻止

「最终选择:」 iframe + 跨域绕过

2. 如何处理跨域限制?

问题

很多网站设置了 X-Frame-Options: DENY,禁止被 iframe 嵌入。

解决方案

使用 Chrome Extension 的 declarativeNetRequest API,在响应到达浏览器前移除这些响应头。

{
"action": {
        "type": "modifyHeaders",
        "responseHeaders": [ { "header": "X-Frame-Options", "operation": "remove" }]
        }
}

「原理:」

服务器返回响应    ↓X-Frame-Options: DENY    ↓插件拦截并移除响应头    ↓浏览器接收到无限制的响应    ↓iframe 成功加载 ✅

安装使用

安装:开发者模式加载

  1. 打开 Chrome 浏览器
  2. 访问 chrome://extensions/
  3. 开启「开发者模式」
  4. 点击「加载已解压的扩展程序」
  5. 选择disguise-extension文件夹

用法

  1. 「访问任意网站」(如掘金)
  2. 「按 Alt+Q」
  3. 「页面瞬间变成百度首页」
  4. 「再按 Alt+Q 恢复」

自定义伪装网站

修改 disguise.js 中的配置:

const config = {  url: 'https://www.baidu.com/',     // 伪装的网站  title: '百度一下',                  // 标签页标题};
❝

「重要提醒:」 此插件仅供学习交流,请勿在工作时间摸鱼!🤣

❞

如果觉得对您有帮助,欢迎点交流哦!

图片
点击"阅读原文"了解详情~