基于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);
相关推荐
hashiqimiya6 小时前
html实现右上角有个图标,鼠标移动到该位置出现手型,点击会弹出登录窗口。
前端·html
BillKu9 小时前
Vue3 中使用 DOMPurify 对渲染动态 HTML 进行安全净化处理
前端·安全·html
BUG创建者17 小时前
html获取16个随机颜色并不重复
css·html·css3
DevilSeagull18 小时前
JavaScript WebAPI 指南
java·开发语言·javascript·html·ecmascript·html5
面向星辰19 小时前
html中css的四种定位方式
前端·css·html
IT利刃出鞘1 天前
HTML--最简的二级菜单页面
前端·html
yume_sibai1 天前
HTML HTML基础(4)
前端·html
面向星辰1 天前
html音视频和超链接标签,颜色标签
前端·html·音视频
信看1 天前
实用 html 小工具
前端·css·html
magnet1 天前
用img标签渲染的svg VS 直接使用svg标签,有什么区别?
前端·html