基于Web Component实现防篡改水印

Web Component?

这篇文章的关键可以分为3个部分,Web Component + 防篡改 + 水印生成 。在使用Web Component之前,我已经通过MutationObserver实现了防篡改水印的第一版。但在了解到Web Component的特性之后,我认为也许用Web Component也不错,因为Web Component内部有钩子天然支持被篡改时被触发,用来防篡改非常方便。下面也会对MutationObserver与Web Component的实现做比较。

设计目标

设计一个hook用于插入水印至ref指定的组件中,被插入的水印节点应当不可篡改。

TypeScript 复制代码
const { data: username } = useRequest(...)
const ref = useWatermark(username); // 水印组件会插入ref.current对应的DOM节点

return <div ref={ref}>
    {children}
</div>

水印生成原理

创建一个div节点,将背景设置为一个透明的水印图片,将z-index设置为10000叠加在站点上即可实现水印效果。

生成水印图

水印图可以在前端直接生成,可以使用canvas通过命令绘制,或预声明svg字符串。我选用的是svg字符串生成,因为使用svg清晰度较高,也比较简单。将svgString定义好,再转换成base64即可

TypeScript 复制代码
        const svgString = `<svg xmlns="http://www.w3.org/2000/svg" width="400px" height="146px">
        <text x="10px" y="73px"
            text-anchor="start"
            transform="rotate(-20 0 73)"
            fill="rgba(40, 47, 56, 0.03)"
            font-weight="500"
            font-size="14"
            font-family="Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif"
        >
            ${content}
        </text>
        <text x="210px" y="146px"
            text-anchor="start"
            transform="rotate(-20 200 146)"
            fill="rgba(40, 47, 56, 0.03)"
            font-weight="500"
            font-size="14"
            font-family="Helvetica Neue,Helvetica,Arial,PingFang SC,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,sans-serif"
        >
            ${content}
        </text>
    </svg>`;
        const background = 'data:image/svg+xml;base64,' + window.btoa(svgString);

CSS与DIV布局

注意所有CSS都应该直接定义在div节点上,并且加上!important,这样能有效避免样式被覆盖。

TypeScript 复制代码
position: absolute !important; 
top: 0px !important; 
right: 0px !important; 
bottom: 0px !important; 
left: 0px !important; 
overflow: hidden !important; 
display: block !important; 
opacity: 1 !important; 
z-index: 10000 !important; 
pointer-events: none !important; // 可以防止鼠标事件被水印节点拦截
background: url(${background});
TypeScript 复制代码
const node = document.createElement('div');
watermarkRef.current = node;
node.setAttribute(
    'style',
    `position: absolute !important; top: 0px !important; right: 0px !important; bottom: 0px !important; left: 0px !important; overflow: hidden !important; display: block !important; opacity: 1 !important; z-index: 10000 !important; pointer-events: none !important; background: url(${background});`
);

防篡改原理

MutationObserve

TypeScript 复制代码
const observer = new MutationObserver(function (entries) {
    // 因为MutationObserve没办法监听自己的属性是否被修改,自己是否被移除,只能监听父组件
    // 通过对父组件观测,entry.type = 'attributes'可以判断水印节点的属性是否被修改
    // entry.type = 'childList' 可以判断水印节点是否被移除
    for (const entry of entries) {
        if (
            (entry.type === 'attributes' && entry.target === watermarkRef.current) ||
            (entry.type === 'childList' && Array.from(entry.removedNodes).includes(watermarkRef.current))
        ) {
            removeWatermark();
            generateWaterMark();
            return;
        }
    }
});

const config = { attributes: true, childList: true, subtree: true };
observer.observe(
    parentNode, // 水印挂载到的父组件
    config
);

Web Component

使用Web Component的好处就是可以直接绑定回调函数,检测自己是否被篡改或移除。

TypeScript 复制代码
class WatermarkElement extends HTMLElement {
  static observedAttributes = ["style"]; // 需要观测的属性

  constructor() {
    super();
  }

  connectedCallback() {
    // 当组件挂载至dom中,可以用来初始化
  }

  disconnectedCallback() {
    // 当组件从document中移除,用于检测水印被删除
  }

  adoptedCallback() {
    // 当组件在document中被移动
    // 若触发则表示水印被移动,可能失效
  }

  attributeChangedCallback(name, oldValue, newValue) {
    // 若触发表示style属性被操作,可能是恶意篡改
  }
}

customElements.define("watermark-element", MyCustomElement);
相关推荐
生涯にわたる学び3 分钟前
数据库02 网页html01 day44
数据库·html
剪刀石头布啊4 小时前
iframe通信、跨标签通信的常见方案
前端·javascript·html
无羡仙6 小时前
当点击链接不再刷新页面
前端·javascript·html
典学长编程8 小时前
前端开发(HTML,CSS,VUE,JS)从入门到精通!第二天(CSS)
前端·javascript·css·html
xingba9 小时前
学习 TreeWalker api 并与普通遍历 DOM 方式进行比较
javascript·api·dom
oioihoii13 小时前
理想I8对撞乘龙卡车,AI基于数学和物理的角度如何看?
html
鹦鹉0071 天前
SpringMVC的基本使用
java·spring·html·jsp
朴shu1 天前
Luckysheet 打印终极指南(预览视图+打印功能) : 2025 最新实现
前端·javascript·html
暮星1 天前
这次一定要讲清 ASCII & Unicode!!!
前端·javascript·html
杨超越luckly1 天前
HTML应用指南:利用GET请求获取全国小米之家门店位置信息
前端·arcgis·html·数据可视化·shp