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 相关文章推荐
JavaScript 语法集锦 脚本之家基础推荐
Nov 15 Javascript
js控制的回到页面顶端goTop的代码实现
Mar 20 Javascript
Select标签下拉列表二级联动级联实例代码
Feb 07 Javascript
jQuery源码分析之Callbacks详解
Mar 13 Javascript
node.js操作mongodb学习小结
Apr 25 Javascript
Google 地图类型详解及示例代码
Aug 06 Javascript
js date 格式化
Feb 15 Javascript
jQuery实现jQuery-form.js实现异步上传文件
Apr 28 jQuery
利用Node.js了解与测量HTTP所花费的时间详解
Sep 22 Javascript
Bootstarp在pycharm中的安装及简单的使用方法
Apr 19 Javascript
jquery轻量级数字动画插件countUp.js使用详解
Oct 17 jQuery
微信h5静默和非静默授权获取用户openId的方法和步骤
Jun 08 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
DedeCms模板安装/制作概述
2007/03/11 PHP
php 无限级分类,超级简单的无限级分类,支持输出树状图
2014/06/29 PHP
PHP中单例模式的使用场景与使用方法讲解
2019/03/18 PHP
利用WebBrowser彻底解决Web打印问题(包括后台打印)
2009/06/22 Javascript
javascript的console.log()用法小结
2012/05/31 Javascript
IE6下opacity与JQuery的奇妙结合
2013/03/01 Javascript
当鼠标移动到图片上时跟随鼠标显示放大的图片效果
2013/06/06 Javascript
jQuery实现将页面上HTML标签换成另外标签的方法
2015/06/09 Javascript
AngularJS入门教程之更多模板详解
2016/08/19 Javascript
node.js入门教程之querystring模块的使用方法
2017/02/27 Javascript
web前端vue之vuex单独一文件使用方式实例详解
2018/01/11 Javascript
vue和webpack项目构建过程常用的npm命令详解
2018/06/15 Javascript
Node.js log4js日志管理详解
2018/07/31 Javascript
BootStrap table实现表格行拖拽效果
2018/12/01 Javascript
vuejs2.0运用原生js实现简单拖拽元素功能
2020/08/21 Javascript
微信小程序实现复选框效果
2018/12/28 Javascript
如何利用vue+vue-router+elementUI实现简易通讯录
2019/05/13 Javascript
发布订阅模式在vue中的实际运用实例详解
2019/06/09 Javascript
微信小程序自定义菜单切换栏tabbar组件代码实例
2019/12/30 Javascript
JavaScript实现简易计算器小功能
2020/10/22 Javascript
5款非常棒的Python工具
2018/01/05 Python
详解Python核心编程中的浅拷贝与深拷贝
2018/01/07 Python
浅谈pandas中DataFrame关于显示值省略的解决方法
2018/04/08 Python
Python用Try语句捕获异常的实例方法
2019/06/26 Python
在PyCharm的 Terminal(终端)切换Python版本的方法
2019/08/02 Python
Python使用mongodb保存爬取豆瓣电影的数据过程解析
2019/08/14 Python
python打造爬虫代理池过程解析
2019/08/15 Python
解决django的template中如果无法引用MEDIA_URL问题
2020/04/07 Python
python3+selenium获取页面加载的所有静态资源文件链接操作
2020/05/04 Python
Pyqt助手安装PyQt5帮助文档过程图解
2020/11/20 Python
类、抽象类、接口的差异
2016/06/13 面试题
高级文秘工作总结的自我评价
2013/09/28 职场文书
办理护照介绍信
2014/01/16 职场文书
公司前台接待岗位职责
2015/04/03 职场文书
Vue中插槽slot的使用方法与应用场景详析
2021/06/08 Vue.js
nodejs利用readline提示输入内容实例代码
2021/07/15 NodeJs