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 相关文章推荐
jQuery Ajax之$.get()方法和$.post()方法
Oct 12 Javascript
避免 showModalDialog 弹出新窗体的原因分析
May 31 Javascript
纯JS实现的批量图片预览加载功能
Aug 14 Javascript
70+漂亮且极具亲和力的导航菜单设计国外网站推荐
Sep 20 Javascript
利用window.name实现windowStorage代码分享
Jan 02 Javascript
JS调用页面表格导出excel示例代码
Mar 18 Javascript
jQuery ajax serialize() 方法使用示例
Nov 02 Javascript
AngularJS进行性能调优的7个建议
Dec 28 Javascript
javascript中错误使用var造成undefined
Mar 31 Javascript
JavaScript数据存储 Cookie篇
Jul 02 Javascript
微信小程序表单弹窗实例
Jul 19 Javascript
angular组件间通讯的实现方法示例
May 07 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
MySQL授权问题总结
2007/05/06 PHP
又一个php 分页类实现代码
2009/12/03 PHP
php删除页面记录 同时刷新页面 删除条件用GET方式获得
2012/01/10 PHP
浅谈PHP链表数据结构(单链表)
2016/06/08 PHP
php源码的安装方法和实例
2019/09/26 PHP
function, new function, new Function之间的区别
2007/03/08 Javascript
JS图片根据鼠标滚动延时加载的实例代码
2013/07/13 Javascript
JS打开新窗口防止被浏览器阻止的方法
2015/01/03 Javascript
JQuery简单实现锚点链接的平滑滚动
2015/05/03 Javascript
JavaScript中数组继承的简单示例
2015/07/29 Javascript
javascript使用输出语句实现网页特效代码
2015/08/06 Javascript
黑帽seo劫持程序,js劫持搜索引擎代码
2015/09/15 Javascript
jquery判断复选框是否被选中的方法
2015/10/16 Javascript
Node.js程序中的本地文件操作用法小结
2016/03/06 Javascript
JS插件plupload.js实现多图上传并显示进度条
2016/11/29 Javascript
微信小程序实现瀑布流布局与无限加载的方法详解
2017/05/12 Javascript
vue webuploader 文件上传组件开发
2017/09/23 Javascript
jQuery替换节点元素的操作方法
2018/03/18 jQuery
Vue.js点击切换按钮改变内容的实例讲解
2018/08/22 Javascript
详解vue项目中调用百度地图API使用方法
2019/04/25 Javascript
js实现页面图片消除效果
2020/03/24 Javascript
vue中全局路由守卫中替代this操作(this.$store/this.$vux)
2020/07/24 Javascript
详解JavaScript自定义函数
2020/07/29 Javascript
python实现文本进度条 程序进度条 加载进度条 单行刷新功能
2019/07/03 Python
pytorch实现mnist分类的示例讲解
2020/01/10 Python
python 爬虫爬取京东ps4售卖情况
2020/12/18 Python
解决tensorflow模型压缩的问题_踩坑无数,总算搞定
2021/03/02 Python
介绍一下write命令
2012/09/24 面试题
老教师工作总结的自我评价
2013/09/27 职场文书
竞聘医务工作人员的自我评价分享
2013/11/04 职场文书
大三学生做职业规划:给未来找个方向
2014/02/24 职场文书
医生爱岗敬业演讲稿
2014/08/26 职场文书
2015年九一八事变纪念日演讲稿
2015/03/19 职场文书
2015年市场部工作总结
2015/04/30 职场文书
电台广播稿范文
2015/08/19 职场文书
使用react+redux实现计数器功能及遇到问题
2021/06/02 Javascript