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端全流程系统。