9
头图

foreword

Hello everyone, I'm Lin Sanxin, uses the most and easy-to-understand words to talk about the most difficult knowledge points. is my motto

background

Everyone usually encounters a problem during development or interview - to add watermark to the page, in fact, this is not difficult, but there are some points to pay attention to, so it is necessary to try to do the seemingly simple functions. arrive:

  • 1. Rigorous
  • 2. Security

implement watermark

In fact, it is not difficult to implement watermark, just use custom command + canvas + background-image, and it is very convenient to implement:

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)
  }
}

When using, use v-watermark directly:

<div 
    v-watermark="
    { 
    text: '水印名称',
    textColor: 'rgba(180, 180, 180, 0.3)' 
    }
    "
  >

The effect obtained is as follows:

Malicious modification

We have completed the watermark function, but let's think about it, what is the use of the watermark? Or why do we want to watermark a page? The answer is: Anti-counterfeiting

Yes, our watermarks are there to prevent counterfeiting, but does it really prevent counterfeiting like we just did? Let's recall our watermarking idea just now:

  • Step 1: Create a canvas and draw a watermark
  • Step 2: Create a watermark container div tag
  • Step 3: Convert the canvas to the image link and assign it to the background-image style attribute of the div tag
  • Step 4: Put the watermark container div under the target element

It seems to have completed the watermark function, but in fact is full of flaws ! ! ! for example:

  • 1. Review the element to modify the background-image property of the container div to be empty

  • 2. Inspect the element and delete the container div

If anyone with ulterior motives does both of these things, it will cause the watermark we just made on our page to disappear! ! !

So we have to monitor the malicious behavior of these people, so what should we do? MutationObserver appeared! ! !

MutationObserver

For the specific usage of MutationObserver , you can go to CDN to see, here I will briefly talk about its function: Monitor the changes of DOM elements

Yes, what it does is: monitors changes in DOM elements , so he can prevent those malicious users from destroying the watermark, because as we just said, malicious users can use the following two methods to destroy the watermark:

  • 1. Review the element to modify the background-image property of the container div to be empty
  • 2. Inspect the element and delete the container div

Both of these points involve the modification of the DOM, so they will trigger the monitoring of MutationObserver , so we can use MutationObserevr to monitor. Here are two methods using its instance:

  • observe : Enable monitoring of DOM changes
  • disconnect : Stop monitoring DOM changes
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
  },
}

Now, if you modify style or delete the container div, the watermark will be regenerated, so malicious users cannot succeed! ! ! Of course, there may be loopholes, we can give suggestions! !

Epilogue

I'm Lin Sanxin, an enthusiastic front-end rookie programmer. If you are motivated, like the front-end, and want to learn the front-end, then we can make friends and fish together haha, touch the fish group, add me, please note [Si No]

image.png


Sunshine_Lin
2.1k 声望7.1k 粉丝