写在前面大家好,我是凯丽,欢迎关注我的“博客”,里面满是好东西。本文所讨论的“前端水印防篡改”方法,本来是想作为专利提交给公司的,写之前在国内的专利检索网站上搜索了一下,发现一模一样的方案已经有了被申请。是字节跳动小伙伴今年年初提交的。既然坑已经被占了,我就不滚了。但是考虑到这个解决方案简单有效,所以在这里分享给大家。水印的实现方案前端水印的需求一直存在,出现频率最高的场景就是防止公司内部系统的信息泄露。如果放在前端实现的话,主流的实现方式分为以下两类:SVG/PNG等图片结合CSS背景属性Canvas。比如打开一个知名的在线素材编辑网站,查看其水印的实现方法,就是第一种方法。好家伙,重要的有这么多,足以看出这种写法前端对这个水印的重视。这里我只截图了CSS,对应的HTML是一个指定背景图片的div元素。另外一种基于Canvas的水印也不难理解,直接画出来就搞定了,这里不再赘述。水印破解前端沉寂了很久,直到有人打开了Devtools……一旦打开浏览器的开发者工具,直接修改元素的CSS属性就可以直接破解页面上的水印。如果水印是基于Canvas绘制的,看起来很难,但实际上,如果在控制台的元素区直接暴力删除整个Canvas元素,水印将不复存在。水印反破解回想一下,下面刚刚提到的水印破解方案要么是通过修改元素的CSS属性,要么是直接修改DOM结构,那么能不能试着用爱心去影响用户,让他不去修改呢?当然不是。人机交互中有一个原则,就是“用最大的恶意来揣测你的用户”。但是仔细想想,用户无论是修改CSS还是DOM,都必须打开浏览器的devtools。可以不让用户打开devtools吗?当然。禁止用户打开devtools以windows为例,如果用户想打开控制台,有两种方式:键盘F12鼠标右键选择inspectelement这个好办://PreventF12eventdocument.addEventListener('keydown',event=>{return123!==event.keyCode||event.returnValue=false;});//阻止鼠标右键事件document.addEventListener('contextmenu',event=>{returnevent.returnValue=假;});这确实可以通过下载完全屏蔽尝试打开devtools的用户,但是有点简单粗暴。毕竟鼠标右键后除了“审查元素”还有其他浏览器功能。过于“一刀切”会伤害用户体验。监听devtools打开事件遗憾的是,浏览器不提供原生的devtools打开事件,但是我们可以用曲线救国:通过查看浏览器可见区域与浏览器窗口的差异来判断用户是否打开了devtools。事实上,devtools-detect是一个在Github上拥有1.5k+star的开源解决方案,它就是这样做的。核心实现也很简单:constresize=()=>{constthreshold=200;constwidth=window.outerWidth-window.innerWidth>threshold;constheight=window.outerHeight-window.innerHeight>threshold;if(width||height){console.log('控制台打开,用户准备破解水印!!!');}}resize();window.addEventListener('resize',resize);但是这个方案有一个很大的漏洞:只能用来检测浏览器页面内嵌的devtools何时打开,但是现在几乎所有的浏览器都提供了在新窗口打开devtools的功能,所以这个检测很容易被绕过。MutationObserver走到这一步,是时候给出最好的方案了(我开头提到的专利方案):基于MutationObserver的元素属性变化监控。MutationObserver接口提供了监视对DOM树所做更改的能力。它旨在替代旧的MutationEvents功能,该功能是DOM3事件规范的一部分。总之,MutationObserver可以监听DOM元素的任何属性的变化,如果需要的话,还可以监听其子元素的变化。这不就是我们需要的吗?当用户通过devtools修改水印元素的属性时,MutationObserver可以及时通知我们,以便我们第一时间恢复我们的水印。需要注意的一点是MutationObserver监听的是元素的属性,也就是attributes,所以我们的css样式应该作为元素的style属性嵌入到HTML中。下面的代码是这个方案的具体实现://
