禁止角色 权限 F12 邮件 浏览器检查 定时
import { addListener, launch, crashBrowserCurrentTab, isLaunch, stop, setDetectDelay } from 'devtools-detector';
import {getRoles, createNotification} from "@/utils/common";
import {getToken} from "@/utils/auth";
/**
* 调试保护工具 - 用于防止网站被调试
*/
const debugProtection = {
// 允许调试的角色
allowedRoles: ['admin', 'developer', 'debugger'],
// 是否已初始化
isInitialized: false,
// 是否已检测到开发者工具打开
isDevToolsOpen: false,
/**
* 检查用户是否有调试权限
* @returns {boolean} 是否有调试权限
*/
hasDebugPermission() {
try {
const roles = getRoles() || [];
console.log('%c当前用户角色:', 'color:blue;font-size:24px;', roles);
// // 开发环境默认允许调试
// if (process.env.NODE_ENV === 'development') {
// console.log('%c开发环境,允许调试', 'color:green;font-size:24px;');
// return true;
// }
// 检查是否有任一允许调试的角色
return Array.isArray(roles) && roles.some(role => this.allowedRoles.includes(role));
} catch (e) {
console.error('获取角色权限时出错:', e);
// 出错时默认允许访问,避免误封正常用户
return true;
}
},
/**
* 初始化调试保护
*/
init() {
// 检查用户是否已登录
if (getToken() === null || getToken() === '' || getToken() === undefined) {
console.log('%c未登录,跳过调试保护!', 'color:red;font-size:24px;font-weight:bold;');
// 设置一个观察器来监听登录状态变化
this.setupLoginObserver();
return;
}
// 如果用户有调试权限,则不启用保护
if (this.hasDebugPermission()) {
console.log('%c调试权限已启用,开发者工具可正常使用', 'color:green;font-size:24px;');
return;
}
console.log('%c调试保护已启用,禁止使用开发者工具', 'color:red;font-size:24px;');
// 确保之前的检测已停止
if (isLaunch()) {
stop();
}
// 重置开发者工具状态
this.isDevToolsOpen = false;
this.setupDevToolsDetection();
// this.disableRightClick();
this.disableDevToolsShortcuts();
this.setupPeriodicCheck();
// 控制台警告
this.setConsoleWarning();
// 标记为已初始化
this.isInitialized = true;
},
/**
* 设置登录观察器,在用户登录后立即启用保护
*/
setupLoginObserver() {
// 使用setInterval定期检查token状态
const loginCheckInterval = setInterval(() => {
// 如果已经初始化了,则不再检查
if (this.isInitialized) {
clearInterval(loginCheckInterval);
return;
}
// 检查是否已登录
if (getToken() !== null && getToken() !== '' && getToken() !== undefined) {
console.log('%c检测到登录,正在启用调试保护...', 'color:orange;font-size:24px;');
this.init(); // 重新初始化
clearInterval(loginCheckInterval); // 清除定时器
}
}, 1000); // 每秒检查一次
},
/**
* 检查开发者工具是否已经打开
*/
checkIfDevToolsOpen() {
// 使用储存的状态判断开发者工具是否打开
if (this.isDevToolsOpen && !this.hasDebugPermission()) {
console.clear();
console.log('%c检测到未授权的调试工具!', 'color:red;font-size:24px;font-weight:bold;');
this.blockUnauthorizedDebugging();
}
},
/**
* 设置开发者工具检测
*/
setupDevToolsDetection() {
// 清除任何现有的监听器,确保不会重复注册
stop();
// 添加监听器
addListener((isOpen, detail) => {
// 更新开发者工具状态
this.isDevToolsOpen = isOpen;
// 如果有调试权限,则不进行拦截
if (isOpen && !this.hasDebugPermission()) {
console.clear();
console.log('%c禁止调试!系统将显示警告并阻止操作。', 'color:red;font-size:24px;font-weight:bold;');
console.log('%c检测到的详情:', 'color:orange;font-size:16px;', detail);
// 立即阻止未授权的调试
this.blockUnauthorizedDebugging();
}
});
// 设置检测延迟为500毫秒,提高响应速度
setDetectDelay(500);
// 启动检测
launch();
console.log('%c调试检测已启动', 'color:orange;font-size:16px;');
},
/**
* 禁用右键菜单
*/
disableRightClick() {
document.addEventListener('contextmenu', (e) => {
// 如果有调试权限,则不阻止右键菜单
if (this.hasDebugPermission()) {
return true;
}
e.preventDefault();
createNotification('禁止使用右键菜单', 'warning', 2000, {
containerId: 'debug-notification-container'
});
return false;
});
},
/**
* 禁用开发者工具快捷键
*/
disableDevToolsShortcuts() {
document.addEventListener('keydown', (e) => {
if (e.key === 'F12' ||
(e.ctrlKey && e.shiftKey && e.key === 'I') ||
(e.ctrlKey && e.shiftKey && e.key === 'J') ||
(e.ctrlKey && e.key === 'U')) {
// 如果有调试权限,则不阻止快捷键
if (this.hasDebugPermission()) {
return true;
}
e.preventDefault();
createNotification('禁止使用开发者工具', 'warning', 2000, {
containerId: 'debug-notification-container'
});
return false;
}
});
},
/**
* 设置定期检查
*/
setupPeriodicCheck() {
setInterval(() => {
// 如果未登录,跳过检查
if (getToken() === null || getToken() === '' || getToken() === undefined) {
return;
}
// 如果有调试权限,则不进行检查
if (this.hasDebugPermission()) {
return;
}
// 确保检测器处于运行状态
if (!isLaunch()) {
// 重新启动检测
this.setupDevToolsDetection();
}
// 重新设置控制台警告
this.setConsoleWarning();
// 检查是否打开了开发者工具
this.checkIfDevToolsOpen();
}, 1000); // 每秒检查一次
},
/**
* 设置控制台警告
*/
setConsoleWarning() {
// 如果有调试权限,则不设置警告
if (this.hasDebugPermission()) {
return;
}
console.clear();
console.log('%c警告!', 'color:red;font-size:50px;font-weight:bold;');
console.log('%c这是一个私有页面功能。如果有人告诉您复制粘贴这里的内容,以启用某个功能或"黑入"某人的账号,这是一个骗局。这样做会让他们能够访问您的账号。', 'color:red;font-size:24px;');
},
/**
* 阻止未授权的调试
*/
blockUnauthorizedDebugging() {
// 创建全屏覆盖层,但不关闭页面
this.createBlockingOverlay();
// 记录事件(可选,如果需要)
console.warn('检测到未授权的调试尝试,已显示警告');
},
/**
* 创建阻止调试的全屏覆盖层
*/
createBlockingOverlay() {
// 检查是否已经存在覆盖层,避免重复创建
if (document.getElementById('debug-protection-overlay')) {
return document.getElementById('debug-protection-overlay');
}
// 创建覆盖层
const overlay = document.createElement('div');
overlay.id = 'debug-protection-overlay';
// 设置全屏覆盖样式 - 使用动态渐变背景
overlay.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(-45deg, #1a2a3a, #2d3a4a, #2c3e50, #34495e);
background-size: 400% 400%;
animation: gradientAnimation 15s ease infinite;
z-index: 999999;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
overflow: auto;
`;
// 添加CSS动画
const style = document.createElement('style');
style.id = 'debug-protection-style';
style.textContent = `
@keyframes gradientAnimation {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.03); }
100% { transform: scale(1); }
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
@keyframes strongShake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-10px); }
20%, 40%, 60%, 80% { transform: translateX(10px); }
}
@keyframes warningPulse {
0% { transform: scale(1); background-color: rgba(255, 50, 50, 0.25); }
50% { transform: scale(1.05); background-color: rgba(255, 50, 50, 0.35); }
100% { transform: scale(1); background-color: rgba(255, 50, 50, 0.25); }
}
@keyframes successPulse {
0% { transform: scale(1); background-color: rgba(40, 167, 69, 0.15); }
50% { transform: scale(1.03); background-color: rgba(40, 167, 69, 0.25); }
100% { transform: scale(1); background-color: rgba(40, 167, 69, 0.15); }
}
@keyframes float {
0% { transform: translateY(0px); }
50% { transform: translateY(-10px); }
100% { transform: translateY(0px); }
}
@keyframes particleFloat {
0% {
transform: translateY(100vh) rotate(0deg);
opacity: 0;
}
10% {
opacity: 0.8;
}
90% {
opacity: 0.8;
}
100% {
transform: translateY(-100px) rotate(360deg);
opacity: 0;
}
}
#debug-protection-overlay {
animation: fadeIn 0.5s ease-out;
}
.security-card {
background: rgba(25, 31, 41, 0.9);
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(78, 110, 142, 0.2);
padding: 30px;
text-align: center;
width: 90%;
max-width: 600px;
margin: 20px;
color: #e6e6e6;
display: flex;
flex-direction: column;
gap: 15px;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
}
.security-icon {
font-size: 50px;
margin-bottom: 5px;
animation: float 3s ease-in-out infinite;
display: inline-block;
}
.security-title {
font-size: 24px;
font-weight: 600;
margin: 0;
color: #fff;
}
.security-message {
font-size: 16px;
line-height: 1.5;
margin: 0;
color: #ccd7e6;
}
.security-description {
font-size: 14px;
line-height: 1.5;
margin: 0;
color: #a3b5cc;
}
.security-badges {
display: flex;
justify-content: center;
gap: 10px;
margin: 5px 0;
flex-wrap: wrap;
}
.security-badge {
background: rgba(59, 89, 152, 0.3);
padding: 6px 12px;
border-radius: 50px;
font-size: 13px;
color: #ccd7e6;
display: flex;
align-items: center;
gap: 6px;
}
.security-status {
padding: 10px 15px;
border-radius: 6px;
margin: 5px 0;
font-size: 14px;
text-align: center;
}
.status-warning {
color: #ff8c94;
background: rgba(220, 53, 69, 0.15);
border-left: 3px solid #dc3545;
animation: pulse 2s infinite;
}
.status-success {
color: #8eff9e;
background: rgba(40, 167, 69, 0.15);
border-left: 3px solid #28a745;
animation: successPulse 2s infinite !important;
}
.security-button {
background: #3b5998;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
margin-top: 5px;
}
.security-button:hover {
background: #4c70ba;
}
.security-button:active {
transform: translateY(1px);
}
.security-footer {
font-size: 12px;
color: #6c8aae;
line-height: 1.5;
margin-top: 5px;
}
.particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
z-index: -1;
}
.particle {
position: absolute;
width: 4px;
height: 4px;
background: rgba(255, 255, 255, 0.6);
border-radius: 50%;
animation: particleFloat 6s linear infinite;
}
@media (max-height: 600px) {
.security-card {
padding: 20px;
gap: 10px;
}
.security-icon {
font-size: 40px;
margin-bottom: 0;
}
.security-badges {
margin: 0;
}
.security-footer {
margin-top: 0;
}
}
@media (max-width: 480px) {
.security-card {
padding: 20px 15px;
}
.security-badges {
gap: 5px;
}
.security-badge {
padding: 5px 10px;
font-size: 12px;
}
}
`;
document.head.appendChild(style);
// 创建粒子效果容器
const particles = document.createElement('div');
particles.className = 'particles';
// 添加多个粒子
for (let i = 0; i < 20; i++) {
const particle = document.createElement('div');
particle.className = 'particle';
// 随机位置
particle.style.left = ${Math.random() * 100}%
;
// 随机大小
const size = Math.random() * 4 + 2;
particle.style.width = ${size}px
;
particle.style.height = ${size}px
;
// 随机透明度
particle.style.opacity = (Math.random() * 0.5 + 0.3).toString();
// 随机动画延迟
particle.style.animationDelay = ${Math.random() * 5}s
;
// 随机动画持续时间
particle.style.animationDuration = ${Math.random() * 10 + 5}s
;
particles.appendChild(particle);
}
overlay.appendChild(particles);
// 创建内容容器
const card = document.createElement('div');
card.className = 'security-card';
// 创建图标
const icon = document.createElement('div');
icon.className = 'security-icon';
icon.textContent = '🛡️';
// 创建标题
const title = document.createElement('h1');
title.className = 'security-title';
title.textContent = '安全保护已启动';
// 创建消息
const message = document.createElement('div');
message.className = 'security-message';
message.textContent = '检测到未授权的调试行为!系统已自动启动保护机制。';
// 创建说明文本
const description = document.createElement('div');
description.className = 'security-description';
description.textContent = '为保障系统安全,当前页面功能已被限制。请关闭开发者工具后继续使用。';
// 创建徽章容器
const badgesContainer = document.createElement('div');
badgesContainer.className = 'security-badges';
// 创建徽章
const badges = [
{ icon: '🔒', text: '安全防护' },
{ icon: '⚡', text: '实时监控' },
{ icon: '🔍', text: '异常检测' }
];
badges.forEach(badge => {
const badgeElement = document.createElement('span');
badgeElement.className = 'security-badge';
badgeElement.innerHTML = ${badge.icon} ${badge.text}
;
badgesContainer.appendChild(badgeElement);
});
// 创建状态信息容器
const statusContainer = document.createElement('div');
statusContainer.className = 'security-status status-warning';
statusContainer.id = 'debug-status-container';
// 创建状态信息
const statusMsg = document.createElement('div');
statusMsg.id = 'debug-status-message';
statusMsg.textContent = '检测到开发者工具正在运行,请关闭后继续。';
// 将状态信息添加到容器
statusContainer.appendChild(statusMsg);
// 创建返回按钮
const backButton = document.createElement('button');
backButton.className = 'security-button';
backButton.textContent = '我已关闭开发工具';
// 点击返回按钮时检查开发者工具是否已关闭
backButton.addEventListener('click', () => {
// 使用储存的状态判断开发者工具是否仍然打开
if (this.isDevToolsOpen) {
// 如果开发者工具仍然打开,则显示提示消息
statusMsg.textContent = '⚠️ 请先关闭开发者工具,然后再尝试返回应用!';
statusContainer.className = 'security-status status-warning';
// 增强的抖动动画效果
statusContainer.style.animation = 'strongShake 0.8s ease-in-out, warningPulse 1.5s infinite';
// 添加临时的高亮样式
statusContainer.style.backgroundColor = 'rgba(255, 50, 50, 0.25)';
statusContainer.style.borderLeftWidth = '5px';
statusContainer.style.fontSize = '16px';
statusContainer.style.fontWeight = 'bold';
// 在动画结束后恢复正常的脉冲动画,但保留高亮样式
setTimeout(() => {
statusContainer.style.animation = 'warningPulse 1.5s infinite';
}, 800);
// 添加额外的视觉提示 - 闪烁边框
card.style.boxShadow = '0 0 15px rgba(255, 0, 0, 0.7)';
setTimeout(() => {
card.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.3)';
}, 1500);
} else {
// 如果开发者工具已关闭,立即更新状态为成功
statusMsg.textContent = '✅ 未检测到开发者工具,您现在可以安全返回应用。';
statusContainer.className = 'security-status status-success';
statusContainer.style.animation = 'successPulse 2s infinite';
statusContainer.style.backgroundColor = 'rgba(40, 167, 69, 0.15)';
statusContainer.style.borderLeftWidth = '3px';
statusContainer.style.fontSize = '14px';
statusContainer.style.fontWeight = 'normal';
// 给用户一点时间看到成功状态,然后再关闭覆盖层
setTimeout(() => {
// 如果开发者工具已关闭,则移除覆盖层
if (overlay.parentNode) {
document.body.removeChild(overlay);
}
if (document.getElementById('debug-protection-style')) {
document.head.removeChild(document.getElementById('debug-protection-style'));
}
// 移除任何检测循环
if (this._devToolsCheckInterval) {
clearInterval(this._devToolsCheckInterval);
this._devToolsCheckInterval = null;
}
}, 1000); // 延迟1秒关闭,让用户看到成功状态
}
});
// 创建页脚
const footer = document.createElement('div');
footer.className = 'security-footer';
footer.innerHTML = `
<p>只有管理员、开发者和调试者角色可以访问开发者工具</p>
<p>如需帮助,请联系系统管理员</p>
`;
// 组合元素
card.appendChild(icon);
card.appendChild(title);
card.appendChild(message);
card.appendChild(description);
card.appendChild(badgesContainer);
card.appendChild(statusContainer);
card.appendChild(backButton);
card.appendChild(footer);
overlay.appendChild(card);
// 添加到文档
document.body.appendChild(overlay);
// 启动持续检查开发者工具状态的循环
this._devToolsCheckInterval = setInterval(() => {
const statusContainer = document.getElementById('debug-status-container');
const statusMsg = document.getElementById('debug-status-message');
if (!statusMsg || !statusContainer) {
clearInterval(this._devToolsCheckInterval);
this._devToolsCheckInterval = null;
return;
}
// 使用储存的状态判断开发者工具是否仍然打开
if (this.isDevToolsOpen) {
statusMsg.textContent = '检测到开发者工具仍在运行,请关闭后再继续。';
statusContainer.className = 'security-status status-warning';
// 确保警告状态下的样式正确
statusContainer.style.animation = 'pulse 2s infinite';
statusContainer.style.backgroundColor = 'rgba(220, 53, 69, 0.15)';
statusContainer.style.borderLeftWidth = '3px';
statusContainer.style.fontSize = '14px';
statusContainer.style.fontWeight = 'normal';
} else {
statusMsg.textContent = '✅ 未检测到开发者工具,您现在可以安全返回应用。';
statusContainer.className = 'security-status status-success';
// 重置所有可能被警告状态修改过的内联样式
statusContainer.style.animation = 'successPulse 2s infinite';
statusContainer.style.backgroundColor = 'rgba(40, 167, 69, 0.15)';
statusContainer.style.borderLeftWidth = '3px';
statusContainer.style.fontSize = '14px';
statusContainer.style.fontWeight = 'normal';
// 确保卡片样式恢复正常
const card = document.querySelector('.security-card');
if (card) {
card.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.3)';
}
}
}, 1000);
// 确保内容适应窗口大小
const adjustCardSize = () => {
const viewportHeight = window.innerHeight;
const card = document.querySelector('.security-card');
if (card) {
// 如果卡片高度超过视口高度的90%,添加滚动样式
if (card.offsetHeight > viewportHeight * 0.9) {
card.style.maxHeight = ${viewportHeight * 0.9}px
;
card.style.overflowY = 'auto';
} else {
card.style.maxHeight = '';
card.style.overflowY = '';
}
}
};
// 初始调整和窗口大小变化时调整
adjustCardSize();
window.addEventListener('resize', adjustCardSize);
return overlay;
}
};
export default debugProtection;