likes
comments
collection
share

关于水印你需要知道哪些

作者站长头像
站长
· 阅读数 18

水印

添加水印目的是为了保护网站或应用中的内容,防止未经授权的复制、转载和篡改。水印通常以文字、图像或其它可识别的标识形式嵌入到页面上,可以包含版权信息、作者姓名、网站名称等。以下是一些加水印的目的:

  1. 版权保护:通过在内容上添加水印,可以提醒用户该内容受版权保护,并对未经授权的使用者发出警告。

  2. 防止盗用和侵权:水印可以在被复制或截屏后仍然保留,使得未经授权的使用者无法去除水印或重复使用内容。

  3. 内容追踪:通过在每个用户独特的水印信息中添加标识,可以追踪和识别泄露或恶意传播的内容,帮助监测和处理侵权行为。

  4. 品牌推广:水印可以包含网站或公司的名称、标志等,提升品牌知名度和宣传效果。

内容完整性确认:水印可以用来验证内容的真实性和完整性,确保内容未经修改或篡改。

社区水印插件

在强大的前端社区中,好多插件可以供直接使用。

如果使用react框架开发可以直接使用ant.design Watermark水印组件里面支持文字水印图片水印两种。而且能监听你更改dom能给你立即生产一个新的水印,传送门

关于水印你需要知道哪些

如果你是jsvue等开发强烈推荐使用Watermark

关于水印你需要知道哪些

盲水印

在看Watermark的时候,作者在配置项中提到了这个概念。

盲水印的好处是可以在不影响用户体验的情况下,将唯一的标识信息嵌入到内容中,帮助保护版权,防止未经授权的复制和侵权行为。

简单来说就是设置一个很小的透明度比如0.005,让人肉眼看不到,但是可以通过调亮底部对比度让水印显示出来。我也百度了这个加水印方式是不是不可破解,得到的答案就是在通过用摄像头多次转拍也会失效。

以下是截图用cssmix-blend-mode: color-burn; 这个属性 相当于对图片加上一个滤镜,调亮底部对比度让水印显示出来看到盲水印的。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <style>
       .box {
         width: 800px;
         height: 500px;
         position: absolute;
         top: 0;
         left: 0;
         background: red;
         mix-blend-mode: color-burn;
         z-index: 9;
       }

       img {
        width: 800px;
         height: 500px;
         position: absolute;
        top: 0;
        left: 0;
       }
    </style>
</head>
<body>
    <div class="box">
    </div>  
    <img src="./1.png" alt="" srcset="">
</body>
</html>

前端实现水印的几种方式

  1. 重复的dom元素覆盖实现;
<!DOCTYPE html> 
<html> 
    <head> 
        <meta charset="utf-8"> 
        <title></title> 
        <style> 
            #watermark-box { 
                position: fixed; 
                top: 0; 
                bottom: 0; 
                left: 0; 
                right: 0; 
                font-size: 24px; 
                font-weight: 700; 
                display: flex; 
                flex-wrap: wrap; 
                overflow: hidden; 
                user-select: none; 
                pointer-events: none; 
                opacity: 0.1; 
                z-index: 999;
            } 
            .watermark { 
                text-align: center; 
            } 
        </style> 
    </head> 
    <body> 
        <div id="watermark-box"> 
        </div> 
        <script> 
            function doWaterMark(width, height, content) { 
                let box = document.getElementById("watermark-box"); 
                let boxWidth = box.clientWidth, 
                    boxHeight = box.clientHeight; 
                for (let i = 0; i < Math.floor(boxHeight / height); i++) { 
                    for (let j = 0; j < Math.floor(boxWidth / width); j++) { 
                        let next = document.createElement("div") 
                        next.setAttribute("class", "watermark") 
                        next.style.width = width + 'px' 
                        next.style.height = height + 'px' 
                        next.innerText = content 
                        box.appendChild(next) 
                    } 
                } 
            } 
            window.onload = doWaterMark(300, 100, '水印123') 
        </script> 
    </body> 
</html>
  1. canvas输出背景图;
<!DOCTYPE html>
<html>
<head>
  <title>Sloped Multiline Text Watermark Example</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }
    canvas {
      position: fixed;
      top: 0;
      left: 0;
      pointer-events: none; /* 确保水印不会干扰用户交互 */
    }

    #watermark-conatiner {
        width: 800px;
        height: 800px;
    }
  </style>
</head>
<body>
<div id="watermark-conatiner"></div>
</body>

<script>

const DOM_ID = "watermark-conatiner";

/**
 * 水印的默认属性
 */
const DEFAULT_OPTIONS = {
  text: "测试水印1111111",
  width: 520, // 水印块的宽度
  height: 280, // 水印块的高度
  rotate: 20, // 水印块的旋转角度
  fontSize: 28, // 文字大小
  color: "#666", // 文字颜色
  opacity: "0.3", // 遮罩层的透明度
  zIndex: "9999999999", // 遮罩层的层级
};

class Watermark {
  options = {};
  observer = null;

  /**
   * 生成水印
   */
  static setWaterMark = (options = {}) => {
    const waterDom = document.getElementById(DOM_ID);
    if (waterDom !== null) {
      // 每次重新绘制之前, 需要判断是否已经存在, 如果存在了就先删除, 再来重新绘制
      document.body.removeChild(waterDom);
    }

    const latestOptions = { ...DEFAULT_OPTIONS, ...options };
    this.options = latestOptions;

    const {
      text,
      width, // 宽度是根据提供的文字大小和文字长度计算出来的, 这里就用不上了
      height, // 水印块的高度
      rotate, // 水印块的旋转角度
      fontSize, // 文字大小
      color, // 文字颜色
      opacity, // 遮罩层的透明度
      zIndex, // 遮罩层的层级
    } = latestOptions;

    const len = text.length;
    const canvas = document.createElement("canvas");
    canvas.width = len * fontSize;
    canvas.height = height + fontSize * 2.8;

    const context = canvas.getContext("2d");
    context.translate(0, canvas.height / 2);
    context.rotate((-rotate * Math.PI) / 180);
    context.font = `${fontSize}px Vedana`; // 设置字体
    context.fillStyle = color; // 设置文字颜色

    // 将需要的文本, 绘制到canvas上面
    context.fillText(text, 10, canvas.height / 2 - 100);

    // 生成水印遮罩层
    const div = document.createElement("div");
    div.id = DOM_ID;
    div.style.pointerEvents = "none";
    div.style.position = "fixed";
    div.style.zIndex = zIndex;
    div.style.left = "-32%";
    div.style.top = "-32%";
    div.style.opacity = opacity;
    div.style.width = "150%";
    div.style.height = "150%";
    div.style.background = `url('${canvas.toDataURL("images/png")}')repeat left top`;

    document.body.appendChild(div);

    /**
     * 监听水印的dom变化
     */
    this.observeDomChange(div);
  };

  /**
   * 去除水印
   */

  static removeWatermark = () => {
    const dom = document.getElementById(DOM_ID);
    if (dom !== null) {
      document.body.removeChild(dom);
    }
  };

  /**
   * 监听dom变化, 防止水印被篡改
   */
  static observeDomChange = (waterMarkDom, options) => {
    const callback = (mutationsList, observer) => {
      for (const mutation of mutationsList) {
        /**
         * 水印节点的属性发生了变动
         */
        if (mutation.target === waterMarkDom) {
          this.setWaterMark(); // 重新生成水印
          observer.disconnect(); // 停止观察
        }

        /**
         * 强行手动删除了水印节点
         */
        if (mutation.removedNodes.length && mutation.removedNodes[0] === waterMarkDom) {
          this.setWaterMark(this.options); // 重新生成水印
          observer.disconnect();
        }
      }
    };

    this.observer = new MutationObserver(callback);

    /** 监听body */
    this.observer.observe(document.querySelector("body"), {
      attributes: true, // 观察属性变动
      childList: true, // 观察目标子节点的变化,是否有添加或者删除
      subtree: true, // 观察后代节点,默认为 false
    });
  };
}

Watermark.setWaterMark();

</script>
</html>

MutationObserver是变动观察器,字面上就可以理解这是用来观察节点变化的。Mutation Observer API 用来监视 DOM 变动,DOM 的任何变动,比如子节点的增减、属性的变动、文本内容的变动,这个 API 都可以得到通知。

设置了MutationObserver之后也只是相对安全了一些,还是可以通过控制台禁用js来跳过我们的监听,总体来说在单纯的在前端页面上加水印总是可以通过一些骚操作来跳过的,防君子不防小人,防外行不防内行。

  1. 图片水印

以一个图片作为水印,如下图使用一个LOGO作为水印,就不再单独实现了,跟上面文本基本一样,就是把文本换成了个image。

关于水印你需要知道哪些

结语

防君子不防小人,防外行不防内行。

参考文章: