前端水印实现方案

1. 背景

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

2. 方式

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

  1. 前端实现的特点

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

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

3. 前端实现方案

3.1 重复的dom元素覆盖实现

  • 在页面上覆盖一个蒙层,设置蒙层的透明度,设置pointer-events为none, 实现点击穿透
  • 生成一个水印片,上面是显示水印的文字,我们可以给文字加一个旋转,和 userSelect 属。 性,让此时的文字无法被选中
  • 在蒙层上重复循环生成水印片
javascript 复制代码
 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;样式实现填满整个屏幕的效果

ini 复制代码
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. 水印破解

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

  • 删除对应 dom 节点
  • 设置对应 dom 节点的 css 样式

5. 水印防御

MutationObserver

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

主要观察的有三点

  • 水印元素本身是否被移除
  • 水印元素属性是否被篡改(display: none ...)
  • 水印元素的子元素是否被移除和篡改 (element生成的方式 )
kotlin 复制代码
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并替换原来的图片路径

ini 复制代码
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 三种元素构成。当我们把其中的一个分量修改,人的肉眼是很难看出其中的变化。

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

8. 团队介绍

智慧家技术平台-应用软件框架开发」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

相关推荐
To_OC7 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic7 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy7 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志8 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble9 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯10 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜10 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司11 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g11 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森11 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http