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 相关文章推荐
用js得到网页中所有的div的id
Oct 19 Javascript
JavaScript小技巧 2.5 则
Sep 12 Javascript
HTML5使用DeviceOrientation实现摇一摇功能
Jun 05 Javascript
每天一篇javascript学习小结(属性定义方法)
Nov 19 Javascript
js 右侧浮动层效果实现代码(跟随滚动)
Nov 22 Javascript
JavaScript实现简单Tip提示框效果
Apr 20 Javascript
ES5学习教程之Array对象
Apr 01 Javascript
微信小程序 setData的使用方法详解
Apr 20 Javascript
JS遍历JSON数组及获取JSON数组长度操作示例【测试可用】
Dec 12 Javascript
js中的reduce()函数讲解
Jan 18 Javascript
Vue中jsx不完全应用指南小结
Nov 01 Javascript
在vue中配置不同的代理同时访问不同的后台操作
Sep 11 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
PHP表单提交表单名称含有点号(.)则会被转化为下划线(_)
2011/12/14 PHP
安装apache2.2.22配置php5.4(具体操作步骤)
2013/06/26 PHP
PHP中使用imagick生成PSD文件缩略图教程
2015/01/26 PHP
php获取今日开始时间和结束时间的方法
2017/02/27 PHP
javascript 有用的脚本函数
2009/05/07 Javascript
jQuery ready函数滥用分析
2011/02/16 Javascript
JS上传图片前的限制包括(jpg jpg gif及大小高宽)等
2012/12/19 Javascript
jquery网页元素拖拽插件效果及实现
2013/08/05 Javascript
全面解析Bootstrap表单使用方法(表单样式)
2015/11/24 Javascript
javascript绘制漂亮的心型线效果完整实例
2016/02/02 Javascript
Node.js利用js-xlsx处理Excel文件的方法详解
2017/07/05 Javascript
js事件委托和事件代理案例分享
2017/07/25 Javascript
JS实现生成由字母与数字组合的随机字符串功能详解
2018/05/25 Javascript
在node中使用jwt签发与验证token的方法
2019/04/03 Javascript
Vue项目中配置pug解析支持
2019/05/10 Javascript
js实现固定区域内的不重叠随机圆
2019/10/24 Javascript
Vue中常用rules校验规则(实例代码)
2019/11/14 Javascript
《javascript设计模式》学习笔记四:Javascript面向对象程序设计链式调用实例分析
2020/04/07 Javascript
js校验开始时间和结束时间
2020/05/26 Javascript
python批量导出导入MySQL用户的方法
2013/11/15 Python
Python设计模式之桥接模式原理与用法实例分析
2019/01/10 Python
Django--权限Permissions的例子
2019/08/28 Python
Python绘制三角函数图(sin\cos\tan)并标注特定范围的例子
2019/12/04 Python
python 实现turtle画图并导出图片格式的文件
2019/12/07 Python
AmazeUI 手机版页面的顶部导航条Header与侧边导航栏offCanvas的示例代码
2020/08/19 HTML / CSS
岗位职责范本
2013/11/23 职场文书
大学生毕业的自我评价分享
2014/01/02 职场文书
幼儿园门卫岗位职责
2014/02/14 职场文书
销售助理岗位职责
2014/02/21 职场文书
学习十八大演讲稿
2014/09/15 职场文书
作风转变年心得体会
2014/10/22 职场文书
护士2014年终工作总结
2014/11/11 职场文书
2015年“7.11”世界人口日宣传活动方案
2015/05/06 职场文书
中学生运动会广播稿
2015/08/19 职场文书
如何写好一份优秀的工作总结?
2019/06/21 职场文书
创业计划书之健康营养产业
2019/10/15 职场文书