Appearance
BlindWatermark 加密隐形盲水印
c-blind-watermark 用于给敏感页面添加可溯源的隐形盲水印。组件会把服务端生成的短 token,或本地传入的 key/value 数据,编码成肉眼难以察觉的像素扰动,并重复铺满页面容器。发生截图或拍屏泄露后,可以把图片上传到配套服务端尝试解码,追溯对应用户或业务信息。组件会默认加入当前时间(精确到秒);客户端 IP 建议通过服务端 GET /watermark/client-info 获取后传入 clientIp。
重要说明
opacity 只控制可见调试层,默认 0 表示不显示肉眼可见文字水印;真正写入截图的隐形水印由 strength 控制。如果把 strength 设为 0,截图里就不会有可恢复信息。
生产推荐
正式项目推荐使用服务端生成的短 token,前端只负责嵌入 token。真实用户信息、业务信息和密钥保存在服务端 PostgreSQL 中,避免把生产密钥暴露在浏览器。服务端生成 token 时会默认加入 watermarkTime 当前时间(精确到秒)和服务端识别到的 clientIp。
关于本机 IP
现代浏览器出于隐私限制,无法可靠直接读取当前电脑本机 / 局域网 IP。组件提供 clientIp 属性,请通过服务端接口、登录态或业务系统获取后传入;本项目服务端提供 GET /watermark/client-info 返回服务端看到的客户端 IP。
基础用法(生产推荐)
动态配置预览
可以直接修改配置项查看水印覆盖、可见调试层和嵌入强度变化。生产环境通常保持 opacity = 0,这里只是为了观察效果默认打开调试层。
简单模式
如果只是内部 demo 或低安全要求场景,也可以直接传入 data + secretKey。组件会默认追加 watermarkTime 当前时间(精确到秒),并在浏览器端使用 Web Crypto 对数据加密后再嵌入。
vue
<c-blind-watermark
:data="{
userId: 'u_10001',
userName: '张三',
department: '研发部'
}"
secret-key="local-demo-secret"
>
<SensitiveDashboard />
</c-blind-watermark>注意
浏览器端密钥会出现在前端代码或运行环境中,不能作为生产级安全方案。正式项目请使用后端生成的 token。
可见调试层
opacity 只影响可见调试文字,方便调试水印覆盖范围;真实隐形水印不受该值影响。
vue
<c-blind-watermark
token="cwb1_xxx"
debug-content="DEBUG WATERMARK"
:opacity="0.12"
/>服务端接口示例
获取服务端识别到的客户端 IP:
bash
curl http://localhost:3000/watermark/client-info生成 token:
bash
curl -X POST http://localhost:3000/watermark/tokens \
-H "Content-Type: application/json" \
-d '{"data":{"userId":"u_10001","documentId":"doc_888"},"subject":"u_10001","purpose":"dashboard-trace"}'验证 token:
bash
curl -X POST http://localhost:3000/watermark/tokens/verify \
-H "Content-Type: application/json" \
-d '{"token":"cwb1_xxx"}'上传截图解码:
bash
curl -X POST http://localhost:3000/watermark/decode \
-F "image=@screenshot.png"API
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| data | 简单模式水印信息,支持 key/value | Record<string, string | number | boolean | null | Array> | {} |
| secretKey | 简单模式浏览器端加密密钥,不建议生产使用 | String | '' |
| token | 服务端生成的短 token,优先级高于 data + secretKey | String | '' |
| clientIp | 客户端 IP。浏览器无法可靠直接读取,建议通过服务端接口获取后传入 | String | '' |
| strength | 真实隐形水印嵌入强度,值越大越容易解码,也越可能被肉眼察觉 | Number | 0.035 |
| opacity | 可见调试层透明度,默认不显示可见文字水印 | Number | 0 |
| tileSize | 重复水印块尺寸 | Number | 384 |
| cellSize | 编码单元尺寸 | Number | 4 |
| zIndex | 水印层级 | Number | 9 |
| disabled | 是否禁用水印 | Boolean | false |
| debugContent | 可见调试层文字,仅在 opacity > 0 时显示 | String | String[] | '' |
| maxPayloadBytes | 最大 payload 字节数,过长会影响截图解码成功率 | Number | 128 |
| tamperProtect | 是否启用轻量防篡改检测 | Boolean | true |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
| ready | 盲水印图案生成后触发 | { payloadBytes, tileSize, cellSize } |
| error | 生成失败时触发 | { code, message } |
| change | 水印层被删除或修改时触发 | Event |
Expose
| 方法名 | 说明 | 类型 |
|---|---|---|
| regenerate | 手动重新生成盲水印 | () => Promise<void> |
| getPayload | 获取当前嵌入的 payload/token | () => string |
| getPatternUrl | 获取当前盲水印 tile 的 dataURL | () => string |
Slots
| 插槽名 | 说明 |
|---|---|
| default | 需要添加隐形盲水印的页面内容 |
局限性
盲水印用于泄露溯源,不是 DRM,不能阻止截图、裁剪、压缩、模糊、重绘或恶意去除。首版算法主要面向浏览器/系统截图;手机拍屏受透视、摩尔纹、压缩、模糊、反光和低光照影响,解码结果应以置信度为准。