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 Event学习第十一章 按键的检测
Feb 10 Javascript
javascript 放大镜 v1.0 基于Yui2 实现的放大镜效果
Mar 08 Javascript
js 禁用只读文本框获得焦点时的退格键
Apr 25 Javascript
基于Jquery实现表格动态分页实现代码
Jun 21 Javascript
php对mongodb的扩展(小试牛刀)
Nov 11 Javascript
一款由jquery实现的整屏切换特效
Sep 15 Javascript
JS实现单行文字不间断向上滚动的方法
Jan 29 Javascript
BootStrap智能表单实战系列(四)表单布局介绍
Jun 13 Javascript
简单理解vue中实例属性vm.$els
Dec 01 Javascript
arcgis for js栅格图层叠加(Raster Layer)问题
Nov 22 Javascript
vue中的过滤器实例代码详解
Jun 06 Javascript
在小程序中推送模板消息的实现方法
Jul 22 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
人工智能开始玩《星际争霸2》 你的操作跟得上吗?
2017/08/11 星际争霸
php实现分页工具类分享
2014/01/09 PHP
php curl中gzip的压缩性能测试实例分析
2016/11/08 PHP
使用php实现网站验证码功能【推荐】
2017/02/09 PHP
php使用str_shuffle()函数生成随机字符串的方法分析
2017/02/17 PHP
php写app接口并返回json数据的实例(分享)
2017/05/20 PHP
PHP微信PC二维码登陆的实现思路
2017/07/13 PHP
关于Laravel参数验证的一些疑与惑
2019/11/19 PHP
jQuery EasyUI 中文API Layout(Tabs)
2010/04/27 Javascript
JavaScript经典效果集锦
2010/07/06 Javascript
javascript-简单的计算器实现步骤分解(附图)
2013/05/30 Javascript
js计算两个时间之间天数差的实例代码
2013/11/19 Javascript
js中的onchange和onpropertychange (onchange无效的解决方法)
2014/03/08 Javascript
Javascript实现的Map集合工具类完整实例
2015/07/31 Javascript
使用jQuery制作浮动工具栏的实例分享
2016/05/13 Javascript
解决微信浏览器Javascript无法使用window.location.reload()刷新页面
2016/06/21 Javascript
Node.js连接postgreSQL并进行数据操作
2016/12/18 Javascript
Vue2.0实现1.0的搜索过滤器功能实例代码
2017/03/20 Javascript
深入理解JavaScript 参数按值传递
2017/05/24 Javascript
Vue学习笔记进阶篇之函数化组件解析
2017/07/21 Javascript
chosen实现省市区三级联动
2018/08/16 Javascript
Vue项目总结之webpack常规打包优化方案
2019/06/06 Javascript
微信JSSDK实现打开摄像头拍照再将相片保存到服务器
2019/11/15 Javascript
详解JavaScript的this指向和绑定
2020/09/08 Javascript
element中Steps步骤条和Tabs标签页关联的解决
2020/12/08 Javascript
Python中if __name__ == &quot;__main__&quot;详细解释
2014/10/21 Python
Python中如何使用if语句处理列表实例代码
2019/02/24 Python
Python 中Django安装和使用教程详解
2019/07/03 Python
python+selenium+PhantomJS抓取网页动态加载内容
2020/02/25 Python
安全员岗位职责
2013/11/11 职场文书
内业资料员岗位职责
2014/01/04 职场文书
四风问题自我剖析材料
2014/10/07 职场文书
教师党员整改措施
2014/10/24 职场文书
详解如何在Canvas中添加事件的方法
2021/04/17 Javascript
Python Pandas pandas.read_sql_query函数实例用法分析
2021/06/21 Python
Python调用腾讯API实现人脸身份证比对功能
2022/04/04 Python