本文将介绍一种前端防篡改的水印实现方式(防君子不防小人)
环境 :vite,vue3,node 18.20.8
一、实现水印
使用canvas画布实现一个水印背景图useWatermarkBg
,该水印接收三个参数text
:水印文字,fontSize
:字体大小,gap
: 字体间距
vue
import { computed } from "vue";
export default function useWatermarkBg(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,
};
});
};
二、实现水印的防篡改
使用MutationObserver
监听需要添加水印的元素,在水印被删除或者修改时重新插入水印元素,实现水印的防篡改功能,具体实现代码如下:
vue
<template>
<div class="watermark-container" ref="parentRef">
<slot></slot>
</div>
</template>
<script setup>
import useWatermarkBg from "./useWatermarkBg";
import { ref, onMounted, onUnmounted } from "vue";
const props = defineProps({
text: {
type: String,
required: true,
default: "watermark",
},
fontSize: {
type: Number,
default: 40,
},
gap: {
type: Number,
default: 20,
},
});
const parentRef = ref(null);
const bg = useWatermarkBg(props);
let div;
// 重置水印
const resetWatermark = () => {
if (!parentRef.value) return;
if (div) {
div.remove();
}
const { base64, size } = bg.value;
div = document.createElement("div");
div.style.position = "absolute";
div.style.backgroundImage = `url(${base64})`;
div.style.backgroundSize = `${size}px ${size}px`;
div.style.backgroundRepeat = "repeat";
div.style.zIndex = 9999;
div.style.inset = 0;
div.style.pointerEvents = "none";
parentRef.value.appendChild(div);
};
const ob = new MutationObserver((entries) => {
for (const entry of entries) {
// 处理div被删除的情况
for (const node of entry.removedNodes) {
if (node === div) {
resetWatermark();
return;
}
}
// 处理div被修改的情况
if (entry.target === div) {
resetWatermark();
}
}
});
onMounted(() => {
resetWatermark();
// 监听parentRef的修改
ob.observe(parentRef.value, {
childList: true, //子元素
subtree: true, //子树
attributes: true, //属性
});
});
onUnmounted(() => {
ob.disconnect();
});
</script>
<style scoped>
.watermark-container {
position: relative;
}
</style>
三、水印使用
vue
<script setup>
import Watermark from "./components/Watermark.vue";
</script>
<template>
<Watermark text="前端防篡改水印" style="background-color: beige">
<div class="content"></div>
</Watermark>
</template>
<style scoped>
.content {
width: 100vw;
height: 100vh;
}
</style>
附上完整代码地址:gitee.com/TriF/tamper...