前端实现水印防篡改

一、问题描述

为了保护版权,有什么时候我们会在网页上面添加水印。添加水印一般是由后端来做的,不过有些网站需要添加水印的保护知识产权类型很多,比如文字、图片、视频。由于后端对于不同操作不同类型的水印和防止水印被篡改比较麻烦,这一操作逐渐让前端来实现。水印很难不被篡改,因此我们必须给水印修改做一定的篡改防范,让用户不那么容易修改。实现的水印效果如下图:

如果你使用ant design开发的话,里面就提供一个水印WaterMark组件。但如果你使用其他UI组件,就需要自己编写水印组件。那么我们应该怎么实现水印防篡改效果?根据我们的需求,需要考虑以下两点:

1、制作水印

2、防止水印被篡改

二、制作水印

运行环境:

[email protected]

Vite + Vue3

我们可以在一个盒子区域内给重复的背景图,背景图可使用canvas来画。为了方便后续添页面加水印,这里封装了一个水印组件。使用该组件必须专递水印文本,水印字体默认30,各水印间隙默认为20。

<script setup>
import {
  ref,
  reactive,
  toRefs,
  defineProps,
  watchEffect,
  onMounted,
  onUnmounted,
} from "vue";
import createWaterMark from "./createWatermark";

const props = defineProps({
  text: {
    type: String,
    required: true,
    default: "watermark",
  },
  fontSize: {
    type: Number,
    default: 30,
  },
  gap: {
    // 间隙
    type: Number,
    default: 20,
  },
});

const wm = createWaterMark(props);
const parentRef = ref(null);
let div;
const flag = ref(0);
watchEffect(() => {
  flag.value;
  if (!parentRef.value) {
    return;
  }
  div && div.remove(); // 消除div原有值
  const { base64, styleSize } = wm.value;
  div = document.createElement("div");
  // div style
  div.style.backgroundImage = `url(${base64})`;
  div.style.backgroundSize = `${styleSize}px ${styleSize}px`;
  div.style.backgroundRepeat = "repeat";
  div.style.zIndex = 9999;
  div.style.position = "absolute";
  div.style.inset = 0; // 子元素与父元素的距离
  div.style.pointerEvents = "none"; // 阻止元素事件
  parentRef.value.appendChild(div); // 把div加到父元素中
});

let ob;
onMounted(() => {
  // 监控元素变化
  ob = new MutationObserver((records) => {
    for (const record of records) {
      // 删除
      for (const dom of record.removedNodes) {
        if (dom === div) { // 如果删除的是水印元素div
          flag.value++; // 只要flag变化就会触发重新生成水印
          return;
        }
      }
      // 修改
      if (record.target === div) {
        // 修改的是水印的div时
        flag.value++;
        return;
      }
    }
  });
  // 监听
  ob.observe(parentRef.value, {
    childList: true, // 是否监控元素内容
    attributes: true, // 是否监控本身属性
    subtree: true, // 是否监控子树
  });
});

onUnmounted(() => {
  ob && ob.disconnect(); // 取消监听
  div = null; // 因为div是一个全局变量,需要回收内存,防止内存泄露
});
</script>

<template>
  <div class="water-mark-warp" ref="parentRef">
    <slot></slot>
    <!-- 添加一个盒子,填满整个区域,给一个重复的水印背景图。 -->
  </div>
</template>

<style lang='scss' scoped>
.water-mark-warp {
  position: relative;
}
</style>

使用canvas画水印背景图的代码如下(createWatermark.js):

import { computed } from "vue";

export default function createWaterMark(props) {
    return computed(() => {
        const canvas = document.createElement("canvas");
        // 显示倍率
        const dpr = window.devicePixelRatio || 1;
        const fontSize = props.fontSize * dpr;
        const font = fontSize + "px serif";
        // 画板对象
        const ctx = canvas.getContext("2d");
        // 获取文字的宽度
        ctx.font = font;
        const { width } = ctx.measureText(props.text);
        const canvasSize = Math.max(100, width) + props.gap * dpr;
        // 设置画板大小
        canvas.width = canvasSize;
        canvas.height = canvasSize;

        ctx.translate(canvas.width / 2, canvas.height / 2);
        ctx.rotate((Math.PI / 180) * -45); // 倒序旋转-45°
        ctx.fillStyle = "rgba(0,0,0,0.3)";
        ctx.font = font;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText(props.text, 0, 0);
        return {
            base64: canvas.toDataURL(),
            size: canvasSize,
            styleSize: canvasSize / dpr,
        };
    });
};

在某一个父组件中使用水印组件示例:

<script lang='ts' setup>
import { ref, reactive, toRefs, defineProps } from "vue";
import WaterMark from "@/components/WaterMark/index.vue";

</script>

<template>
  <div class="index-warp">
    <WaterMark text="Topskys" >
            In order to protect the copyright, when will we add watermark on the web page.
 Adding watermarks is generally done by the back end, but some websites need to add watermarks to protect many types of intellectual property, such as text, pictures, videos.
 Watermark is difficult not to be tampered with, so we must do some tampering prevention 
for watermark modification. Since the back end is more troublesome for different types of 
watermarks and to prevent the watermark from being tampered, this operation is gradually left to the front end以上翻译结果来自有道神经网络翻译(YNMT)·
      <img src="https://x0.ifengimg.com/res/2020/2CDD563B75BF507CAECEBCF7297304BAFD0AA2F8_size119_w1108_h736.jpeg" style="display:block;width:100%;"/>
    </WaterMark>
  </div>
</template>

<style lang='scss' scoped>
.index-warp{
  width:600px;
  height:600px;
  margin: 50px auto;
}
</style>

三、防止水印被篡改

用户可能会右键检查元素并删除水印,所以我们需要监控用户删除或修改水印元素时,重新生成水印元素。那怎么监控用户是否删除或修改了水印元素?如何重新生成水印?

要监控水印元素的变化,这里需要用MutationObserver(),它能够捕获到元素的删除、修改等变化。然后使用watchEffect()重新制作水印。为了防止用户直接修改水印元素样式,其样式是写在watchEffect()回调函数中。

new MutationObserver((records) => {})

watchEffect(() => {})

当用户删除水印元素或修改水印样式时,通过判断MutationObserver捕获到元素的变化,确定是否执行flag.value++,从而触发watchEffect()重新执行绘制水印操作,也就达到了防止水印被篡改的效果。

更多详细内容:

https://v.douyin.com/UHC8eQ4/

猜你喜欢

转载自blog.csdn.net/qq_58062502/article/details/130967380
今日推荐