MutationObserver在页面水印实现起到的作用详解


Posted in Javascript onJuly 07, 2022

背景

大家平时在开发中或者在面试中,难免都会遇到一个问题——给页面加水印,其实这并不难,但是也是有一些注意点的,所以说看似简单的功能,要尽力做到:

  • 1、严谨性
  • 2、安全性

实现水印

其实实现水印并不难,只需要利用自定义指令 + canvas + background-image即可,实现起来也非常方便:

import type { Directive, App } from 'vue'
interface Value {
  font?: string
  textColor?: string
  text?: string
}
const waterMarkId = 'waterMark'
const canvasId = 'can'
const drawWatermark = (el, value: Value) => {
  const {
    font = '16px Microsoft JhengHei',
    textColor = 'rgba(180, 180, 180, 0.3)',
    text = '三心大菜鸟',
  } = value
  // 创建一个canvas标签
  const canvas = document.getElementById(canvasId) as HTMLCanvasElement
  // 如果已有则不再创建
  const can = canvas || document.createElement('canvas')
  can.id = canvasId
  el.appendChild(can)
  // 设置宽高
  can.width = 400
  can.height = 200
  // 不可见
  can.style.display = 'none'
  const ctx = can.getContext('2d')!
  // 设置画布的样式
  ctx.rotate((-20 * Math.PI) / 180)
  ctx.font = font
  ctx.fillStyle = textColor
  ctx.textAlign = 'left'
  ctx.textBaseline = 'middle'
  ctx.fillText(text, can.width / 3, can.height / 2)
  // 水印容器
  const waterMaskDiv = document.createElement('div')
  waterMaskDiv.id = waterMarkId
  // 设置容器的属性样式
  // 将刚刚生成的canvas内容转成图片,并赋值给容器的 background-image 样式
  const styleStr = `
    width: 100%;
    height: 100%;
    position: fixed;
    z-index: -1;
    top: 0;
    left: 0;
    pointer-events: none;
    background-image: url(${can.toDataURL('image/png')})
  `
  waterMaskDiv.setAttribute('style', styleStr)
  // 将水印容器放到目标元素下
  el.appendChild(waterMaskDiv)
  return styleStr
}
const watermarkDirective: Directive = {
  mounted(el, { value }) {
    // 接收styleStr,后面可以用来对比
    el.waterMarkStylestr = drawWatermark(el, value)
  }
}

使用的时候直接以v-watermark来使用:

<div 
    v-watermark="
    { 
    text: '水印名称',
    textColor: 'rgba(180, 180, 180, 0.3)' 
    }
    "
  >

得到的效果如下:

MutationObserver在页面水印实现起到的作用详解

恶意修改

咱们完成了水印功能,但是咱们来想一想,水印有啥用?或者说我们为什么要给一个页面加水印呢?答案就是:防伪

是的,我们的水印是用来防伪的,但是像我们刚刚那么做真的能防伪吗?我们回忆一下我们刚刚的加水印思路:

  • 第一步:创建个canvas且画好水印
  • 第二步:创建个水印容器div标签
  • 第三步:将canvas画布转图片链接,赋值给div标签的background-image样式属性
  • 第四步:将水印容器div放到目标元素之下

看似完成了水印功能,但是其实破绽百出!!!比如:

  • 1、审查元素修改容器div的background-image属性为空

MutationObserver在页面水印实现起到的作用详解

  • 2、审查元素把容器div给删掉

MutationObserver在页面水印实现起到的作用详解

如果一切别有用心的人做了这两件事,这都会导致我们页面上刚刚所做的水印直接消失!!!

MutationObserver在页面水印实现起到的作用详解

所以咱们得监控这些人的恶意行为,那咋做呢?MutationObserver出场了!!!

MutationObserver

MutationObserver的具体用法大家可以去MDN上看,这里我就简言意骇地说一下他的作用:监控DOM元素的变化

是的,它的作用就是:监控DOM元素的变化,所以他能阻止那些恶意用户破坏水印,因为我们刚刚说了,恶意用户可以使用以下两种方法进行破坏水印:

  • 1、审查元素修改容器div的background-image属性为空
  • 2、审查元素把容器div给删掉

而这两点都涉及到DOM的修改,所以都会引起MutationObserver的监控触发,所以咱们可以利用MutationObserevr来监控。这里用到它的实例的两个方法:

  • observe:开启监控DOM变化
  • disconnect:停止监控DOM变化
const watermarkDirective: Directive = {
  mounted(el, { value }) {
    // 接收styleStr,后面可以用来对比
    el.waterMarkStylestr = drawWatermark(el, value)
    // 先定义一个MutationObserver
    el.observer = new MutationObserver(() => {
      const instance = document.getElementById(waterMarkId)
      const style = instance?.getAttribute('style')
      const { waterMarkStylestr } = el
      // 修改样式 || 删除div
      if ((instance && style !== waterMarkStylestr) || !instance) {
        if (instance) {
          // div还在,说明只是修改style
          instance.setAttribute('style', waterMarkStylestr)
        } else {
          // div不在,说明删除了div
          drawWatermark(el, value)
        }
      }
    })
    // 启动监控
    el.observer.observe(document.body, {
      childList: true,
      attributes: true,
      subtree: true,
    })
  },
  unmounted(el) {
    // 指定元素销毁时,记得停止监控
    el.observer.disconnect()
    el.observer = null
  },
}

现在,你修改style或者删除容器div,都会重新生成水印,这样恶意用户就无法得逞啦!!!当然可能还有漏洞,大家可以给给建议!!

以上就是MutationObserver在页面水印实现起到的作用详解的详细内容,更多关于MutationObserver页面水印的资料请关注三水点靠木其它相关文章!


Tags in this post...

Javascript 相关文章推荐
document.designMode的功能与使用方法介绍
Nov 22 Javascript
jQuery Clone Bug解决代码
Dec 22 Javascript
Enter转换为Tab的小例子(兼容IE,Firefox)
Nov 14 Javascript
一个JS函数搞定网页标题(title)闪动效果
May 13 Javascript
wap浏览自动跳转到wap页面的js代码
May 17 Javascript
页面刷新时记住滚动条的位置jquery代码
Jun 17 Javascript
jquery实现动静态条形统计图
Aug 17 Javascript
jQuery 判断是否包含在数组中Array[]的方法
Aug 03 Javascript
EasyUI为Numberbox添加blur事件的方法
Mar 05 Javascript
微信小程序实现页面跳转传值的方法
Oct 12 Javascript
JS简单实现动态添加HTML标记的方法示例
Apr 08 Javascript
element表格翻页第2页从1开始编号(后端从0开始分页)
Dec 10 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
兼容性比较好的PHP生成缩略图的代码
2011/01/12 PHP
PHP逐行输出(ob_flush与flush的组合)
2012/02/04 PHP
PHP exif扩展方法开启详解
2014/07/28 PHP
PHP模块memcached使用指南
2014/12/08 PHP
PHP实现数组的笛卡尔积运算示例
2017/12/15 PHP
PHP中的Iterator迭代对象属性详解
2019/04/12 PHP
PHP基础之输出缓冲区基本概念、原理分析
2019/06/19 PHP
番茄的表单验证类代码修改版
2008/07/18 Javascript
jquery 文本上下无缝滚动,鼠标放上去就停止 小例子
2013/06/05 Javascript
JavaScript 模拟类机制及私有变量的方法及思路
2013/07/10 Javascript
JavaScript用Number方法实现string转int
2014/05/13 Javascript
使用node.js 制作网站前台后台
2014/11/13 Javascript
jquery validation验证表单插件
2017/01/07 Javascript
单击按钮发送验证码,出现倒计时的简单实例
2017/03/17 Javascript
js控制文本框禁止输入特殊字符详解
2017/04/07 Javascript
Centos6.8下Node.js安装教程
2017/05/12 Javascript
详解使用webpack构建多页面应用
2017/12/21 Javascript
解决vue页面DOM操作不生效的问题
2018/03/17 Javascript
关于Mac下安装nodejs、npm和cnpm的教程
2018/04/11 NodeJs
express框架下使用session的方法
2019/07/31 Javascript
Python类属性与实例属性用法分析
2015/05/09 Python
解决Python print输出不换行没空格的问题
2018/11/14 Python
用Python徒手撸一个股票回测框架搭建【推荐】
2019/08/05 Python
python3.x 生成3维随机数组实例
2019/11/28 Python
快速查找Python安装路径方法
2020/02/06 Python
python Canny边缘检测算法的实现
2020/04/24 Python
python使用hdfs3模块对hdfs进行操作详解
2020/06/06 Python
有关HTML5页面在iPhoneX适配问题
2017/11/13 HTML / CSS
html+js 实现markdown编辑器效果
2019/10/23 HTML / CSS
Farfetch中文官网:奢侈品牌时尚购物平台
2020/03/15 全球购物
会计助理岗位职责
2014/02/17 职场文书
实习报告评语
2014/04/26 职场文书
解除财产保全担保书
2014/05/20 职场文书
宣传普通话标语
2014/06/27 职场文书
Python中生成随机数据安全性、多功能性、用途和速度方面进行比较
2022/04/14 Python
python 镜像环境搭建总结
2022/09/23 Python