debugger阻止打开控制台
- 一、为什么需要反调试?
- 二、整体设计思路
- [三、入口集成:条件启用 + 防重复挂载](#三、入口集成:条件启用 + 防重复挂载)
- 四、反调试核心实现
- 五、注意事项

一、为什么需要反调试?
浏览器开发者工具是前端开发的得力助手,但同时也为"不速之客"提供了便利。通过控制台,攻击者可以:
-
动态修改变量、调用函数,绕过前端校验
-
窃取 API 请求参数与响应数据
-
复制敏感 DOM 结构或业务逻辑
-
无限调试,分析代码执行流程
-
虽然无法做到绝对安全,但通过增加调试门槛,可以大幅提升攻击成本,让大多数脚本小子知难而退。
二、整体设计思路
我们设计的反调试模块需满足以下目标:
-
无感知集成:与 Vue 应用生命周期无缝衔接,仅在 production 环境启用。
-
多重检测:结合窗口尺寸差异、元素偏移、定时器等多种手段,提高检测准确率。
-
主动防御:检测到调试后,立即锁屏并注入高频 debugger 指令,干扰调试器执行。
-
延迟挂载:若页面加载时控制台已打开,则暂缓 Vue 应用挂载,防止应用代码被提前断点。
-
资源清理:提供销毁方法,便于组件卸载时释放监听器与定时器。
三、入口集成:条件启用 + 防重复挂载
mian.js
js
// main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import Antd from 'ant-design-vue/es';
import 'ant-design-vue/dist/antd.less';
import { initAntiDebugProtection } from './utils/antiDebug';
/**
* 挂载 Vue 应用的函数(避免重复挂载)
*/
let appInstance = null;
function mountApp() {
// 防止重复挂载
if (appInstance) return;
appInstance = createApp(App);
appInstance.use(router);
appInstance.use(Antd);
appInstance.mount('#app');
console.log('✅ Vue 应用已挂载');
}
// 仅生产环境启用反调试,开发环境直接挂载
if (process.env.NODE_ENV === 'production') {
initAntiDebugProtection(mountApp);
} else {
mountApp();
}
关键点解析:
-
appInstance 单例模式防止热更新时重复挂载。
-
通过 process.env.NODE_ENV 区分环境,开发时避免干扰正常调试。
-
将 mountApp 作为回调传入反调试模块,由模块决定何时真正挂载(若控制台关闭则立即挂载,否则等待关闭)。
四、反调试核心实现
antiDebug.js
js
/**
* 反调试保护模块
* 功能:屏蔽快捷键、检测开发者工具、高频 debugger 干扰、延迟挂载
* 使用:在生产环境调用 initAntiDebugProtection(mountCallback)
*/
let initialized = false; // 防止重复初始化
let debuggerInterval = null; // debugger 定时器句柄
let isOpen = false; // 控制台是否打开
let mountCallback = null; // 挂载回调函数
let isMounted = false; // 是否已完成挂载
/**
* 初始化反调试保护
* @param {Function} onMountReady - 当控制台关闭时执行的回调(通常用于挂载 Vue 应用)
* @returns {Function} destroy - 销毁清理函数
*/
export function initAntiDebugProtection(onMountReady) {
// 1. 防止重复初始化
if (initialized) {
console.warn('[antiDebug] Already initialized');
return;
}
// 2. SSR 兼容:服务端直接返回
if (typeof window === 'undefined' || typeof document === 'undefined') {
return;
}
// 3. 等待 DOM 就绪
if (!document.body) {
window.addEventListener(
'DOMContentLoaded',
() => {
initAntiDebugProtection(onMountReady);
},
{ once: true }
);
return;
}
initialized = true;
mountCallback = onMountReady || (() => {});
// ---------- 4. 屏蔽快捷键 / 右键 ----------
document.addEventListener('contextmenu', (e) => e.preventDefault());
document.addEventListener('keydown', (e) => {
if (
e.key === 'F12' ||
(e.ctrlKey && e.shiftKey && (e.key === 'I' || e.key === 'J')) ||
(e.ctrlKey && e.key === 'u')
) {
e.preventDefault();
return false;
}
});
// ---------- 5. 创建遮罩层 ----------
const overlay = document.createElement('div');
overlay.id = '__anti_debug_overlay';
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
color: white;
font-size: 24px;
display: none;
justify-content: center;
align-items: center;
z-index: 99999;
pointer-events: none;
user-select: none;
font-family: sans-serif;
`;
overlay.textContent = '⚠️ 检测到调试工具,页面已锁定';
document.body.appendChild(overlay);
// ---------- 6. 创建隐藏检测元素 ----------
const detector = document.createElement('div');
detector.id = '__anti_debug_detector';
detector.style.cssText = `
position: fixed;
bottom: 0;
right: 0;
width: 100px;
height: 100px;
z-index: -9999;
opacity: 0;
pointer-events: none;
`;
document.body.appendChild(detector);
// ---------- 7. 核心检测函数 ----------
function checkDevTools() {
// 策略1:窗口尺寸差异检测(停靠模式)
const widthDiff = window.outerWidth - window.innerWidth;
const heightDiff = window.outerHeight - window.innerHeight;
const threshold = 150;
// 策略2:隐藏元素偏移检测(弹出模式)
const rect = detector.getBoundingClientRect();
const elementMoved =
rect.left < 0 ||
rect.top < 0 ||
rect.width !== 100 ||
rect.height !== 100;
const detected =
widthDiff > threshold || heightDiff > threshold || elementMoved;
if (detected) {
// --- 控制台打开 ---
if (!isOpen) {
isOpen = true;
overlay.style.display = 'flex';
// 立即执行一次 debugger(同步)
(function () {})['constructor']('debugger')();
// 启动高频 debugger 循环(每 10ms 打断)
if (!debuggerInterval) {
debuggerInterval = setInterval(() => {
(function () {})['constructor']('debugger')();
}, 10);
}
}
} else {
// --- 控制台关闭 ---
if (isOpen) {
isOpen = false;
overlay.style.display = 'none';
// 停止 debugger 循环
if (debuggerInterval) {
clearInterval(debuggerInterval);
debuggerInterval = null;
}
// 执行挂载回调(如果尚未挂载)
if (!isMounted) {
isMounted = true;
mountCallback();
}
}
}
return detected;
}
// ---------- 8. 立即执行一次同步检测(页面加载瞬间) ----------
const initiallyOpen = checkDevTools();
if (!initiallyOpen) {
// 控制台未打开:立即挂载
isMounted = true;
mountCallback();
} else {
// 控制台已打开:延迟挂载,等待关闭时触发
console.warn('[antiDebug] Console open, mounting delayed.');
}
// ---------- 9. 定时轮询(每 50ms) ----------
setInterval(checkDevTools, 50);
// ---------- 10. 监听窗口 resize(快速响应) ----------
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(checkDevTools, 30);
});
// ---------- 11. 返回销毁函数 ----------
return function destroy() {
if (debuggerInterval) {
clearInterval(debuggerInterval);
debuggerInterval = null;
}
document.getElementById('__anti_debug_overlay')?.remove();
document.getElementById('__anti_debug_detector')?.remove();
initialized = false;
isOpen = false;
isMounted = false;
console.log('[antiDebug] Destroyed');
};
}
核心功能
-
状态管理:防重复初始化、记录调试状态、控制挂载时机。
-
环境适配:兼容 SSR,自动等待 DOM 就绪。
-
快捷拦截:屏蔽右键菜单、F12、Ctrl+Shift+I/J、Ctrl+U。
-
动态 UI:创建遮罩层 + 隐藏探测元素。
-
双模检测:尺寸差异(停靠模式)+ 元素偏移(弹出模式)。
-
主动反制:显示遮罩 + 高频 debugger 循环(每 10ms)。
-
恢复回调:控制台关闭后隐藏遮罩、停止干扰、触发 Vue 挂载。
-
持续监控:50ms 轮询 + resize 防抖响应。
-
资源销毁:清除定时器、移除 DOM、重置标志位。
五、注意事项
性能影响:setInterval 每 50ms 执行一次尺寸计算,且调试状态下每 10ms 执行一次 debugger,对性能有一定损耗,但仅在调试时触发,正常用户无感知。
绕过风险:该方案无法防御高级反反调试手段(如修改 window.outerWidth 的 getter、禁用 debugger 语句等),但足以应对大多数常规尝试。
开发体验:务必仅在 production 环境启用,否则开发时无法使用控制台,影响开发效率。
可扩展性:可根据业务需求调整阈值、检测频率,甚至加入更复杂的指纹校验。