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获取form表单值的代码
Jul 17 Javascript
jQuery 瀑布流 浮动布局(一)(延迟AJAX加载图片)
May 23 Javascript
jquery 利用show和hidden实现级联菜单示例代码
Aug 09 Javascript
jquery快捷动态绑定键盘事件的操作函数代码
Oct 17 Javascript
JS小功能(列表页面隔行变色)简单实现
Nov 28 Javascript
js实现拖拽效果(构造函数)
Dec 14 Javascript
javascript中Date对象的使用总结
Nov 21 Javascript
JS实现仿百度文库评分功能
Jan 12 Javascript
AngularJS中$injector、$rootScope和$scope的概念和关联关系深入分析
Jan 19 Javascript
jquery版轮播图效果和extend扩展
Jul 18 jQuery
微信小程序出现wx.getLocation再次授权问题的解决方法分析
Jan 16 Javascript
微信小程序中wxs文件的一些妙用分享
Feb 18 Javascript
js作用域及作用域链工作引擎
Promise静态四兄弟实现示例详解
Jul 07 #Javascript
Three.js实现雪糕地球的使用示例详解
二维码条形码生成的JavaScript脚本库
Jul 07 #Javascript
JS实现简单的九宫格抽奖
JS实现九宫格拼图游戏
JavaScript实现九宫格拖拽效果
You might like
无数据库的详细域名查询程序PHP版(4)
2006/10/09 PHP
php 上传文件类型判断函数(避免上传漏洞 )
2010/06/08 PHP
php实现根据url自动生成缩略图的方法
2014/09/23 PHP
PHP自定义错误用法示例
2016/09/28 PHP
PHP编程实现脚本异步执行的方法
2017/08/09 PHP
javascript 页面划词搜索JS
2009/09/28 Javascript
基于Jquery的仿Windows Aero弹出窗(漂亮的关闭按钮)
2010/09/28 Javascript
初学js 新节点的创建 删除 的步骤
2011/07/04 Javascript
js实现鼠标拖动图片并兼容IE/FF火狐/谷歌等主流浏览器
2013/06/06 Javascript
js隐式全局变量造成的bug示例代码
2014/04/22 Javascript
Jquery插件分享之气泡形提示控件grumble.js
2014/05/20 Javascript
jQuery焦点图轮播特效代码分享(3款)
2015/09/05 Javascript
原生JS封装Ajax插件(同域、jsonp跨域)
2016/05/03 Javascript
全面了解js中的script标签
2016/07/04 Javascript
NodeJS遍历文件生产文件列表功能示例
2017/01/22 NodeJs
利用pm2部署多个node.js项目的配置教程
2017/10/22 Javascript
Vue全家桶实践项目总结(推荐)
2017/11/04 Javascript
Vue CLI3基础学习之pages构建多页应用
2019/06/02 Javascript
JavaScript使用localStorage存储数据
2019/09/25 Javascript
javascript中的with语句学习笔记及用法
2020/02/17 Javascript
vue addRoutes路由动态加载操作
2020/08/04 Javascript
prettier自动格式化去换行的实现代码
2020/08/25 Javascript
[44:58]2018DOTA2亚洲邀请赛 4.5 淘汰赛 LGD vs Liquid 第二场
2018/04/06 DOTA
python 自动提交和抓取网页
2009/07/13 Python
Python字典及字典基本操作方法详解
2018/01/30 Python
Python cookbook(数据结构与算法)字典相关计算问题示例
2018/02/18 Python
python调用Matplotlib绘制分布点图
2019/10/18 Python
荷兰手表网站:Watch2Day
2018/07/02 全球购物
REN Clean Skincare官网:英国本土有机护肤品牌
2019/02/23 全球购物
MyBag中文网:英国著名的时尚包袋电商零售网站
2020/07/31 全球购物
会展策划与管理专业求职信
2014/06/09 职场文书
2014最新党员违纪检讨书
2014/10/12 职场文书
工会文体活动总结
2015/05/07 职场文书
预备党员表决心的话
2015/09/22 职场文书
Mysql数据库中datetime、bigint、timestamp来表示时间选择,谁来存储时间效率最高
2021/08/23 MySQL
SQL CASE 表达式的具体使用
2022/03/21 SQL Server