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 面向对象 function类
May 13 Javascript
IE6下出现JavaScript未结束的字符串常量错误的解决方法
Nov 21 Javascript
JavaScript检测实例属性, 原型属性
Feb 04 Javascript
基于jQuery实现鼠标点击导航菜单水波动画效果附源码下载
Jan 06 Javascript
jquery获取文档高度和窗口高度汇总
Jan 25 Javascript
AngularJS实现单独作用域内的数据操作
Sep 05 Javascript
全选复选框JavaScript编写小结(附代码)
Aug 16 Javascript
安装vue-cli报错 -4058 的解决方法
Oct 19 Javascript
vue.js 图片上传并预览及图片更换功能的实现代码
Aug 27 Javascript
jQuery zTree树插件的使用教程
Aug 16 jQuery
js实现简单贪吃蛇游戏
May 15 Javascript
React实现类似淘宝tab居中切换效果的示例代码
Jun 02 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
laravel框架添加数据,显示数据,返回成功值的方法
2019/10/11 PHP
js获取IP和PcName(IE)在vs中可用
2013/08/02 Javascript
jQuery选择器querySelector的使用指南
2015/01/23 Javascript
JS+CSS实现TreeMenu二级树形菜单完整实例
2015/09/18 Javascript
基于JavaScript代码实现微信扫一扫下载APP
2015/12/30 Javascript
JavaScript数据推送Comet技术详解
2016/04/07 Javascript
js如何准确获取当前页面url网址信息
2020/09/13 Javascript
jQuery获取radio选中项的值实例
2016/06/18 Javascript
vue.js2.0点击获取自己的属性和jquery方法
2018/02/23 jQuery
mpvue小程序仿qq左滑置顶删除组件
2018/08/03 Javascript
layui实现三级联动效果
2019/07/26 Javascript
JS替换字符串中指定位置的字符(多种方法)
2020/05/28 Javascript
关于element-ui表单中限制输入纯数字的解决方式
2020/09/08 Javascript
vue实现简单全选和反选功能
2020/09/15 Javascript
Python列表(list)常用操作方法小结
2015/02/02 Python
python图像处理之镜像实现方法
2015/05/30 Python
Python常用库推荐
2016/12/04 Python
python+matplotlib绘制饼图散点图实例代码
2018/01/20 Python
使用Python计算玩彩票赢钱概率
2019/06/26 Python
python实现爬取百度图片的方法示例
2019/07/06 Python
python + selenium 刷B站播放量的实例代码
2020/06/12 Python
keras 模型参数,模型保存,中间结果输出操作
2020/07/06 Python
总结Pyinstaller的坑及终极解决方法(小结)
2020/09/21 Python
Python性能测试工具Locust安装及使用
2020/12/01 Python
python实现发送QQ邮件(可加附件)
2020/12/23 Python
为2021年的第一场雪锦上添花:用matplotlib绘制雪花和雪景
2021/01/05 Python
Python中的流程控制详解
2021/02/18 Python
钉钉企业内部H5微应用开发详解
2020/05/12 HTML / CSS
澳大利亚最大的护发和护肤品购物网站:RY
2019/12/26 全球购物
SQL SERVER面试资料
2013/03/30 面试题
体育老师的教学自我评价分享
2013/11/19 职场文书
养殖项目策划书范文
2014/01/13 职场文书
局机关干部群众路线个人对照检查材料思想汇报
2014/10/05 职场文书
社区四风存在问题及整改措施
2014/10/26 职场文书
地震捐款简报
2015/07/21 职场文书
【海涛dota】偶遇拉娜娅 质量局德鲁伊第一视角解说
2022/04/01 DOTA