前端水印进阶:手把手教你打造高安全 Canvas 水印
🛡️ 1. 回顾与展望:从组件到自定义,安全升级!
在上篇博客中,我们一起领略了 Ant Design Vue 水印组件的魅力。它就像一个"傻瓜式"的智能安防系统,即插即用,省心省力,让我们的前端应用轻松拥有了水印功能。然而,我们也提到了它的"小娇气"------对框架的依赖性,以及在面对"专业级"破解者时,防篡改能力略显不足的短板。
那么,有没有一种方案,既能让我们完全掌控水印的每一个细节,又能拥有"金刚不坏之身",让那些试图篡改水印的"黑客"们无从下手呢?答案是肯定的!今天,我们就将揭开前端水印的"硬核"面纱,手把手教你如何利用 Canvas
和 MutationObserver
,打造一个高度自定义、防篡改能力超强的水印系统!
🎨 2. 方案二:自定义 Canvas 水印------硬核玩家的专属!
如果你是一个追求极致、喜欢掌控一切的"硬核玩家",那么自定义 Canvas 水印方案绝对是你的菜!它就像自己动手打造一套独一无二的指纹识别系统,虽然过程可能复杂一些,但最终的安全系数和定制化程度绝对能让你惊艳!

2.1 特点解读:自由、安全、无拘束!
这个方案的特点,简直是为那些对水印有特殊要求,或者对安全性有极高追求的项目量身定制的:
- 完全自定义实现:从水印的形状、颜色、字体,到布局、透明度,甚至连水印的"灵魂"------绘制逻辑,你都可以一手掌控。想怎么画就怎么画,你的水印你做主!
- 具备防篡改机制:这是这个方案最核心的亮点!通过巧妙的监听机制,一旦水印被"动了手脚",它就能像"不死鸟"一样,瞬间恢复原状,让那些试图篡改水印的人无功而返。
- 支持复杂样式定制:想让水印动起来?想让水印根据用户行为变化?想让水印变成一个复杂的图案?只要你的 Canvas 功底足够,一切皆有可能!
- 框架无关,通用性强:不像 Ant Design Vue 方案那样"挑食",这个方案不依赖任何特定的前端框架。无论是 Vue、React 还是 Angular,甚至是纯原生 JavaScript 项目,都能轻松集成,通用性极强。
2.2 核心实现:Canvas 魔法与 DOM 守护者!
自定义 Canvas 水印的核心在于两点:利用 Canvas
绘制水印图案,以及利用 MutationObserver
监听 DOM 变化,实现水印的防篡改。让我们一步步揭开它的神秘面纱。
2.2.1 Canvas 绘制水印图案
首先,我们需要创建一个 Canvas
元素,并在上面绘制我们想要的水印文字或图案。这个 Canvas
元素并不会直接显示在页面上,而是作为水印的"模板"。我们会将 Canvas
绘制的内容转换为图片,然后作为背景图应用到页面的一个 div
元素上。
arduino
// utils/watermark.js
import tool from '@/utils/tool'
export const watermark = {
set: function (text1, text2) {
// 1. 创建 Canvas 元素
const canvas = document.createElement('canvas')
canvas.width = 150
canvas.height = 120
canvas.style.display = 'none' // 隐藏 Canvas 元素
// 2. 获取 2D 绘图上下文
const shuiyin = canvas.getContext('2d')
// 3. 设置文字样式和位置
shuiyin.rotate((-20 * Math.PI) / 180) // 逆时针旋转 20 度,让水印倾斜
shuiyin.translate(-50, 20) // 调整文字的起始绘制位置,使其更居中或符合设计
shuiyin.fillStyle = '#f5f5f5' // 设置文字颜色,这里是浅灰色
shuiyin.font = '100 16px Microsoft JhengHei' // 设置字体样式和大小
// 4. 绘制水印文字
// 在 Canvas 上绘制两行文字,作为水印内容
shuiyin.fillText(text1, canvas.width / 3, canvas.height / 2)
shuiyin.fillText(text2, canvas.width / 3, canvas.height / 2 + 20)
// 5. 创建水印容器
const watermark = document.createElement('div')
// 构建水印容器的样式字符串,使其覆盖整个视口,并设置为重复背景图
const styleStr = `
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 99999; // 确保水印在最上层
pointer-events: none; // 允许鼠标事件穿透水印,不影响页面交互
background-repeat: repeat; // 背景图重复平铺
mix-blend-mode: multiply; // 混合模式,使水印与背景内容融合更自然
background-image: url('${canvas.toDataURL('image/png')}') // 将 Canvas 内容转为 Data URL 作为背景图
`
watermark.setAttribute('style', styleStr) // 设置样式
watermark.classList.add('watermark') // 添加 class,方便识别和操作
document.body.appendChild(watermark) // 将水印容器添加到 body 中
// ... (防篡改监听机制部分将在下一节详细讲解)
},
close: function () {
// 移除水印
let watermark = document.body.querySelector(".watermark")
if (watermark) {
document.body.removeChild(watermark)
}
}
}
代码解析:
- 创建 Canvas 并绘制 :我们创建了一个隐藏的
canvas
元素,通过getContext('2d')
获取 2D 绘图上下文。然后,我们设置了水印的旋转角度 (rotate
)、偏移量 (translate
)、颜色 (fillStyle
) 和字体 (font
)。最后,使用fillText
方法在canvas
上绘制了两行文字。 - 生成 Data URL :
canvas.toDataURL('image/png')
是一个非常关键的方法,它将canvas
上绘制的内容转换为一个 Base64 编码的 PNG 图片数据,这个数据可以直接作为 CSS 的background-image
使用。 - 创建水印容器 :我们创建了一个
div
元素作为水印的容器,并设置了一系列 CSS 样式,使其固定定位、覆盖整个视口、层级最高 (z-index: 99999
),并且最重要的是pointer-events: none
,这样用户就无法通过鼠标点击到水印,不会影响页面的正常交互。background-repeat: repeat
确保水印图案在整个页面平铺。 - 混合模式
mix-blend-mode: multiply
:这是一个 CSS3 属性,它可以让水印与页面内容更好地融合,产生一种"印在纸上"的视觉效果,而不是简单地覆盖在上面。
2.2.2 防篡改机制详解:水印的"不死之身"!
光有水印还不够,如果用户可以轻易地通过开发者工具删除或修改水印,那它的防护作用就大打折扣了。这时候,MutationObserver
就该登场了!它就像一个忠诚的"DOM 守护者",时刻监控着页面的变化,一旦发现水印被"动了手脚",就会立即"修复"它。
dart
// ... (接上一段代码)
// 6. 防篡改监听机制
const observer = new MutationObserver(() => {
// 检查登录状态,如果用户已退出,则关闭水印并停止监听
if (!tool.data.get('TOKEN')) {
this.close()
observer.disconnect()
return
}
const wmInstance = document.body.querySelector('.watermark')
// 检测水印是否被删除或修改
if (!wmInstance || wmInstance.getAttribute('style') !== styleStr) {
if (wmInstance) {
// 样式被修改,重新设置
wmInstance.setAttribute('style', styleStr)
} else {
// 元素被删除,重新添加
if (tool.data.get('TOKEN')) {
document.body.appendChild(watermark) // 重新添加水印元素
} else {
observer.disconnect() // 如果已退出登录,则停止监听
}
}
}
})
// 7. 开始监听 DOM 变化
// 监听 document.body 的属性变化、子树变化和子节点变化
observer.observe(document.body, {
attributes: true, // 监听属性变化,例如 style 属性被修改
subtree: true, // 监听所有后代节点的变化
childList: true // 监听子节点的增删
})
},
// ... (close 方法)
}
防篡改机制解析:
-
MutationObserver
:这是一个现代浏览器提供的 API,用于监听 DOM 树的变化。我们可以配置它监听元素的属性变化 (attributes
)、子节点增删 (childList
),以及整个子树的变化 (subtree
)。 -
监听回调函数 :当
MutationObserver
监听到 DOM 发生变化时,会触发我们定义的回调函数。在这个回调函数中,我们首先检查用户的登录状态,确保只有在登录状态下才维护水印。 -
检测与修复:
- 水印元素被删除 :如果
document.body.querySelector('.watermark')
返回null
,说明水印元素被删除了,我们会判断用户是否登录,如果登录则重新将水印元素添加到body
中。 - 水印样式被修改 :如果水印元素存在,但其
style
属性与我们最初设置的styleStr
不一致,说明水印的样式被修改了。我们会立即重新设置style
属性,将其恢复原状。
- 水印元素被删除 :如果
-
停止监听 :当用户退出登录时,我们会调用
observer.disconnect()
停止监听,避免不必要的性能开销。
通过这种机制,自定义 Canvas 水印方案能够有效地防止水印被删除或篡改,大大提升了水印的安全性。
2.3 使用方法:简单调用,轻松驾驭!
虽然实现过程比较复杂,但使用起来却非常简单。你只需要在需要显示水印的页面或组件中,导入 watermark
工具,然后调用 set
方法即可。需要移除水印时,调用 close
方法。
csharp
// 导入水印工具
import { watermark } from '@/utils/watermark'
// 添加水印,传入两行文字内容
watermark.set('用户姓名', '用户账号')
// 移除水印
watermark.close()
2.4 优点分析:安全、灵活、无所不能!✅
- 高度可定制,样式灵活:想怎么玩就怎么玩,你的水印你做主!
- 防篡改能力强 :
MutationObserver
就像一个忠诚的卫士,时刻守护着你的水印,让它"不死不灭"。 - 框架无关,通用性好:无论你的项目用什么框架,都能轻松集成,没有任何"水土不服"的问题。
- 支持复杂的水印效果:只要你有创意,Canvas 就能帮你实现!
2.5 缺点剖析:门槛较高,性能挑战!❌
- 实现复杂,代码量大:相比 Ant Design Vue 组件方案,这个方案需要你对 Canvas 绘图和 DOM 监听有更深入的理解,代码量也相对较大。
- 需要处理兼容性问题 :虽然
Canvas
和MutationObserver
在现代浏览器中支持良好,但在一些老旧浏览器中可能存在兼容性问题,需要额外处理。 - 性能开销相对较大 :
MutationObserver
会持续监听 DOM 变化,这会带来一定的性能开销。如果页面 DOM 结构非常复杂,或者变化非常频繁,可能会影响页面性能。因此,需要进行适当的优化,例如防抖处理。
总而言之,自定义 Canvas 水印方案是前端水印领域的"硬核武器",它赋予了你极致的控制权和强大的防篡改能力。如果你对水印的安全性有极高要求,或者需要实现一些独特的、复杂的视觉效果,那么这个方案绝对值得你投入时间和精力去探索和实践!