| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- /**
- * 开发者工具保护
- * 检测开发者工具是否打开,如果打开则循环执行 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;
- });
- }
|