javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > 前端水印实现方案

前端水印实现方案详细举例介绍

作者:三翼鸟数字化技术团队

前端水印技术常用于防止信息泄露或追溯数据来源,尤其在后台管理系统、数据可视化平台中应用广泛,这篇文章主要介绍了前端水印实现方案的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

1. 背景

为了防止信息泄露或知识产权被侵犯,我们可以给数据或图片加一层水印,水印能够很好的保护知识产权。

2. 方式

水印的添加根据环境可以分为两大类,前端浏览器环境添加和后端服务环境添加

  1. 前端实现的特点

    1. 可以不占用服务器资源,完全依赖客户端的计算能力,减少服务端压力
    2. 速度快,无论哪种前端的实现方式,性能都是优于后端的
    3. 实现方式简单
    4. 安全系数较低,对于掌握一定前端知识的人来说可以通过各种骚操作跳过水印获取到源文件
  2. 后端实现的特点

    1. 安全,安全,安全
    2. 当遇到大文件密集水印,或是复杂水印,占用服务器内存、运算量,请求时间过长

3. 前端实现方案

3.1 重复的dom元素覆盖实现

 divWaterMark (width, height, content) {
      const waterWrapper = document.createElement('div')
      cssHelper(waterWrapper, {
        position: 'fixed',
        top: '0px',
        right: '0px ',
        bottom: '0px',
        left: '0px',
        overflow: 'hidden',
        display: 'flex',
        'flex-wrap': 'wrap',
        'pointer-events': 'none'
      })
      const waterHeight = width
      const waterWidth = height
      const { clientWidth, clientHeight } = document.documentElement || document.body
      const column = Math.ceil(clientWidth / waterWidth)
      const rows = Math.ceil(clientHeight / waterHeight)

      for (let i = 0; i < column * rows; i++) {
        const wrap = document.createElement('div')
        cssHelper(
          wrap,
          Object.create({
            position: 'relative',
            width: `${waterWidth}px`,
            height: `${waterHeight}px`,
            flex: `0 0 ${waterWidth}px`,
            overflow: 'hidden'
          })
        )
        wrap.appendChild(createItem(content))
        waterWrapper.appendChild(wrap)
      }
      document.body.appendChild(waterWrapper)
    },

3.2 canvas 实现方式

创建一个canvas画布,绘制出一个水印区域,将这个水印通过toDataURL方法输出为一个图片,将这个图片设置为蒙层的背景图,通过图片的backgroud-repeat:repeat;样式实现填满整个屏幕的效果

const createWaterMark = (width, height, content) => {
        const angle = -20
        const txt = content
        const canvas = document.createElement('canvas')
        canvas.width = width
        canvas.height = height
        const ctx = canvas.getContext('2d')
        ctx.clearRect(0, 0, width, height)
        ctx.fillStyle = '#000'
        ctx.globalAlpha = 0.1
        ctx.font = `16px serif`
        ctx.rotate((Math.PI / 180) * angle)
        ctx.fillText(txt, 0, 50)
        return canvas.toDataURL()
      }
      const watermakr = document.createElement('div')
      watermakr.className = 'watermark'
      watermakr.style.backgroundImage = `url(${createWaterMark()})`
      document.body.appendChild(watermakr)
      
<style>
.watermark {
  position: fixed;
  top: 0px;
  right: 0px;
  bottom: 0px;
  left: 0px;
  pointer-events: none;
  background-repeat: repeat;
}
</style>

3.3 svg实现

svg 和 canvas 类似,只不过是生成背景图的方法换成了通过svg生成

4. 水印破解

上面实现水印的方法,都存在一个问题很明显的问题,那就是对于有些前端知识的人来说通过开发者调试工具稍微操作一下,就能够导致水印失效:

5. 水印防御

MutationObserver

MutationObserver 是一个可以监听DOM结构变化的接口,能够监听 DOM 树属性、节点本身、子节点 等的变化。防御思路就是就是使用 MutationObserver 去监听外部对应 water-mark 节点的操作,只要监听到了就重新渲染水印效果即可。

主要观察的有三点

createObserver () {
      // 创建一个观察器实例并传入回调函数
      const observer = new MutationObserver(this.callback)
      observer.disconnect()
      // 以上述配置开始观察目标节点
      observer.observe(targetNode, config)
    },
    // 当观察到变动时执行的回调函数
    callback (mutationsList, observer) {
      const watermark = this.watermakr
      const style = this.style
      const currStyle = watermark.getAttribute('style')

      for (let mutation of mutationsList) {
        // 检测元素样式被修改
        if (
          mutation.type === 'attributes' &&
          mutation.target === watermark &&
          currStyle !== style
        ) {
          watermark.setAttribute('style', style)
        }
        // 检测元素被删除
        mutation.removedNodes.forEach(item => {
          console.log(item.type, item.target, currStyle)
          if (item === watermark) {
            // document.body.appendChild(targetNode)
            this.canvasWaterMark(180, 100, '水印加密')
            this.createObserver()
          }
        })
      }
    }

6. 图片加水印

在图片上加水印的实现思路是,图片加载成功后画到canvas中,随后在canvas中绘制水印,完成后通过canvas.toDataUrl()方法获得base64并替换原来的图片路径

addWaterMarkToImg (imgUrl, cb, content) {
      const img = new Image()
      img.src = imgUrl
      img.crossOrigin = 'anonymous'
      img.onload = function () {
        const canvas = document.createElement('canvas')
        canvas.width = img.width
        canvas.height = img.height
        const ctx = canvas.getContext('2d')

        ctx.drawImage(img, 0, 0)
        ctx.textAlign = 'center'
        ctx.textBaseline = 'middle'
        ctx.font = '20px Microsoft Yahei'
        ctx.fillStyle = 'rgba(184, 184, 184, 0.8)'
        const textX = 100
        const textY = 30
        ctx.fillText(content, img.width - textX, img.height - textY)
        const base64Url = canvas.toDataURL()
        cb && cb(base64Url)
      }
      
     // 给图片加水印
    const url = 'https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/66a8e7cc0a0d4b0ba4130a43c796ad52~tplv-k3u1fbpfcp-zoom-in-crop-mark:4536:0:0:0.image'
    this.addWaterMarkToImg(url, (base64Url) => {
      document.querySelector('img').src = base64Url
    }, '水印加密')

7. 暗水印

暗水印是一种肉眼不可见的水印方式,可以保持图片美观的同时,保护资源版权

原理 :暗水印的生成方式有很多,常见的为通过修改RGB分量值的小量变动、DWT、DCT 和 FFT 等等方法。每个像素点都是由 RGB 三种元素构成。当我们把其中的一个分量修改,人的肉眼是很难看出其中的变化。

简单的说,对图片像素的处理,加密的信息散布在每个像素点上。

到此这篇关于前端水印实现方案的文章就介绍到这了,更多相关前端水印实现方案内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文