Web端的考试系统中,为了防止考生用电脑切到其他页面或软件查资料,一般都会在考试时进入全屏并设置切屏检测功能。这篇文章介绍如何实现类似的全屏和切屏检测,以及如何破解切屏检测。
实现切屏检测其实很简单,我们切屏时都会造成窗口失去焦点,浏览器JavaScript中也有对应的API来判断当前页面是否失去焦点。
下面例子中我们编写了一个简单的页面,点击「开始考试」按钮后,系统会进入全屏状态且监听window
对象的blur
事件,页面失去焦点时对应的事件函数会被回调。
index.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>考试系统</title>
<script src="jquery.min.js"></script>
<script src="index.js"></script>
</head>
<body>
<button id="begin-exam">开始考试</button>
<div id="msg-box"></div>
</body>
</html>
index.js
$(function () {
function launchFullscreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
}
}
$("#begin-exam").click(function () {
launchFullscreen(document.documentElement);
window.addEventListener("blur", function () {
$("#msg-box").append(
"<div>[" +
new Date().toLocaleTimeString() +
"]你切屏了,考试中禁止切屏!</div>"
);
});
});
});
当我们切屏时系统就会给出警告。
实际上,除了window
对象的blur
,我们还有一些其它可以监听的位置来判断是否发生了切屏,例如document
对象的mouseleave
等。
破解切屏检测比较Low但绝对可靠的方式是使用虚拟机,不过虚拟机启动后比较耗系统资源,有些情况下我们也不方便在考试电脑上安装虚拟机软件和里面的操作系统。
实际上,在浏览器中破解切屏检测也十分容易,我们将blur
、mouseleave
等事件的回调函数覆盖掉即可,此外为了将脚本注入到页面,我们可以使用油猴插件来实现。
具体例子代码如下。
// ==UserScript==
// @name Fuck Examination
// @namespace http://tampermonkey.net/
// @version 2024-06-20
// @description try to take over the world!
// @author You
// @match http://*/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(function () {
"use strict";
Object.defineProperty(window, "onblur", {
value: null,
writable: false,
});
Object.defineProperty(document, "onblur", {
value: null,
writable: false,
});
Object.defineProperty(window, "onmouseleave", {
value: null,
writable: false,
});
Object.defineProperty(document, "onmouseleave", {
value: null,
writable: false,
});
Object.defineProperty(window, "onvisibilitychange", {
value: null,
writable: false,
});
Object.defineProperty(document, "onvisibilitychange", {
value: null,
writable: false,
});
const originalEventTargetAddEventListener =
EventTarget.prototype.addEventListener;
const originalHTMLElementAddEventListener =
HTMLElement.prototype.addEventListener;
const originalWindowAddEventListener = Window.prototype.addEventListener;
const originalDocumentAddEventListener = Document.prototype.addEventListener;
function allowCall(eventName) {
return (
eventName !== "visibilitychange" &&
eventName !== "focus" &&
eventName !== "focusin" &&
eventName !== "focusout" &&
eventName !== "blur" &&
eventName !== "mouseleave"
);
}
EventTarget.prototype.addEventListener = function (eventName, eventHandler) {
if (allowCall(eventName)) {
originalEventTargetAddEventListener.call(this, eventName, eventHandler);
}
};
HTMLElement.prototype.addEventListener = function (eventName, eventHandler) {
if (allowCall(eventName)) {
originalHTMLElementAddEventListener.call(this, eventName, eventHandler);
}
};
Window.prototype.addEventListener = function (eventName, eventHandler) {
if (allowCall(eventName)) {
originalWindowAddEventListener.call(this, eventName, eventHandler);
}
};
Document.prototype.addEventListener = function (eventName, eventHandler) {
if (allowCall(eventName)) {
originalDocumentAddEventListener.call(this, eventName, eventHandler);
}
};
})();
启用插件脚本后,前面编写的切屏检测代码就不再起作用了。