From 0e210b90a2042da7db7971925c58194542f29917 Mon Sep 17 00:00:00 2001 From: mubai576 Date: Sun, 30 Nov 2025 10:30:16 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E8=80=85=E5=B7=A5=E5=85=B7=E4=BF=9D=E6=8A=A4=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E9=98=B2=E6=AD=A2=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 6 ++ src/utils/devtools-protection.ts | 158 +++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 src/utils/devtools-protection.ts diff --git a/src/main.ts b/src/main.ts index 16ac610..7a6e4d7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -28,6 +28,9 @@ import ElementIcons from '@/plugins/svgicon'; // permission control import './permission'; +// 开发者工具保护 +import { initDevToolsProtection } from '@/utils/devtools-protection'; + // 国际化 import i18n from '@/lang/index'; @@ -55,3 +58,6 @@ app.use(plugins); directive(app); app.mount('#app'); + +// 初始化开发者工具保护(仅生产环境) +initDevToolsProtection(); diff --git a/src/utils/devtools-protection.ts b/src/utils/devtools-protection.ts new file mode 100644 index 0000000..4efd3b3 --- /dev/null +++ b/src/utils/devtools-protection.ts @@ -0,0 +1,158 @@ +/** + * 开发者工具保护 + * 检测开发者工具是否打开,如果打开则循环执行 debugger 阻止调试 + */ + +// 检测开发者工具是否打开 +function detectDevTools(): boolean { + try { + // 方法1: 检测窗口尺寸差异(最可靠的方法) + const widthThreshold = 160; // 开发者工具最小宽度 + const heightThreshold = 160; // 开发者工具最小高度 + + const widthDiff = window.outerWidth - window.innerWidth; + const heightDiff = window.outerHeight - window.innerHeight; + + if (widthDiff > widthThreshold || heightDiff > heightThreshold) { + return true; + } + + // 方法2: 检测 debugger 执行时间(最准确的方法) + const start = performance.now(); + debugger; // 这个 debugger 用于检测,不会被移除 + const end = performance.now(); + const timeDiff = end - start; + + // 如果 debugger 被跳过(开发者工具关闭),时间差会很小(通常 < 1ms) + // 如果 debugger 暂停(开发者工具打开),时间差会很大(通常 > 100ms) + // 降低阈值以提高检测灵敏度 + if (timeDiff > 10) { + return true; + } + + // 方法3: 检测控制台对象 + let devtoolsDetected = false; + const element = document.createElement('div'); + Object.defineProperty(element, 'id', { + get: function () { + devtoolsDetected = true; + return ''; + } + }); + + // 使用 console 来触发 getter(仅在开发者工具打开时) + try { + console.log(element); + console.clear(); + } catch (e) { + // 忽略错误 + } + + if (devtoolsDetected) { + return true; + } + + // 方法4: 检测控制台是否被重写(开发者工具打开时) + const devtoolsRegex = /./; + // @ts-expect-error - 动态添加属性 + devtoolsRegex.toString = function () { + // @ts-expect-error - 动态添加属性 + this.opened = true; + }; + console.log('%c', devtoolsRegex); + // @ts-expect-error - 检查动态添加的属性 + if (devtoolsRegex.opened) { + return true; + } + } catch (e) { + // 如果检测过程中出错,默认返回 false + return false; + } + + return false; +} + +// 开发者工具保护主函数 +export function initDevToolsProtection(): void { + // 可以通过环境变量控制是否启用 + // 生产环境默认启用,开发环境可以通过 VITE_ENABLE_ANTI_DEBUG=true 来启用测试 + const isProduction = import.meta.env.MODE === 'production'; + const enableAntiDebug = import.meta.env.VITE_ENABLE_ANTI_DEBUG === 'true' || isProduction; + + if (!enableAntiDebug) { + return; + } + + let devToolsOpen = false; + let debuggerInterval: number | null = null; + + // 立即执行一次检测 + const initialCheck = detectDevTools(); + if (initialCheck) { + devToolsOpen = true; + debuggerInterval = window.setInterval(() => { + debugger; // 循环执行 debugger + }, 50); // 更频繁的 debugger,每 50ms 一次 + } + + // 循环检测开发者工具(更频繁的检测) + const checkInterval = setInterval(() => { + const isOpen = detectDevTools(); + + if (isOpen && !devToolsOpen) { + // 开发者工具刚打开 + devToolsOpen = true; + + // 开始循环执行 debugger(更频繁) + if (debuggerInterval === null) { + debuggerInterval = window.setInterval(() => { + debugger; // 循环执行 debugger + }, 50); // 每 50ms 执行一次,更激进 + } + } else if (!isOpen && devToolsOpen) { + // 开发者工具关闭了 + devToolsOpen = false; + + // 停止循环 debugger + if (debuggerInterval !== null) { + clearInterval(debuggerInterval); + debuggerInterval = null; + } + } + }, 500); // 每 500ms 检测一次,更频繁 + + // 页面卸载时清理 + window.addEventListener('beforeunload', () => { + clearInterval(checkInterval); + if (debuggerInterval !== null) { + clearInterval(debuggerInterval); + } + }); + + // 额外的检测:监听窗口大小变化 + let lastWidth = window.innerWidth; + let lastHeight = window.innerHeight; + + window.addEventListener('resize', () => { + const currentWidth = window.innerWidth; + const currentHeight = window.innerHeight; + + // 如果窗口尺寸变化很大,可能是开发者工具打开/关闭 + if (Math.abs(currentWidth - lastWidth) > 200 || Math.abs(currentHeight - lastHeight) > 200) { + // 重新检测 + const isOpen = detectDevTools(); + if (isOpen && !devToolsOpen) { + devToolsOpen = true; + if (debuggerInterval === null) { + debuggerInterval = window.setInterval(() => { + debugger; // 循环执行 debugger + }, 50); // 更频繁的 debugger + } + } + } + + lastWidth = currentWidth; + lastHeight = currentHeight; + }); +} + -- Gitee