首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >#WorkBuddy# #WorkBuddy开发者分享季 JSP管理后台动态水印实战:从零到模块化,带防篡改机制

#WorkBuddy# #WorkBuddy开发者分享季 JSP管理后台动态水印实战:从零到模块化,带防篡改机制

原创
作者头像
用户12592343
修改2026-07-14 17:59:47
修改2026-07-14 17:59:47
1730
举报
文章被收录于专栏:使用经验使用经验

一、一个真实的痛点

上个月我们部门的商户数据管理系统上线后,运营同事反馈了一个问题:截图泄密

场景很简单——内部管理后台有1286条商户敏感信息(姓名、电话、交易流水),有人截个图发到微信群里,数据就出去了。虽然大家都知道不该这么做,但忙起来的时候谁还记得"敏感数据请勿外传"?

传统做法是后台加水印,但 JSP 老项目的方案五花八门:有人用 CSS ::after 伪元素(一调控制台就删),有人后端生成图片覆盖(占带宽、影响加载),有人在 <body> 写死文字(换用户不更新)。

我花半小时用 WorkBuddy 搭了一套模块化动态水印方案,三行代码接入任何 JSP 页面,效果拉满:

  • ✅ 显示当前登录用户 + 时间戳(动态生成,截图可追溯)
  • ✅ Canvas 渲染成背景图,不会干扰页面交互(pointer-events: none
  • ✅ MutationObserver 防篡改(浏览器控制台删不掉)
  • ✅ 模块化设计,watermark-core.js 独立文件,任意 JSP 页面直接引入
  • ✅ 三行代码接入,零侵入原业务逻辑

下面完整复盘实现过程,看完你也能直接用到自己的项目里。


二、技术选型:为什么用 Canvas?

动态水印本质是一个"全屏覆盖层"的问题,业界有三种主流方案:

方案

实现方式

优点

缺点

CSS 伪元素

body::after { content: "xxx"; }

极简,一行CSS

控制台一秒删除;文字不可旋转/多行

SVG 内联

<svg><pattern>...</pattern></svg>

矢量清晰,CSS可控

DOM节点可被直接移除

Canvas → base64背景

JS生成Canvas → toDataURL() → background-image

不可DOM选中;抗锯齿好;支持旋转/多行

需写JS,稍复杂

最终选 Canvas 方案,核心原因两条:

  1. 安全:水印作为背景图存在,控制台删 DOM 节点没用(MutationObserver 还会自动恢复)
  2. 清晰:Canvas 配合 devicePixelRatio 处理 Retina 屏,文字边缘锐利

三、分步实操

3.1 向 WorkBuddy 描述需求

打开 WorkBuddy,输入:

代码语言:javascript
复制
帮我在JSP管理页面加动态水印,要求:
1. 显示当前登录用户名+部门+时间戳
2. 文字倾斜约22度,半透明,铺满整个页面
3. 不能被控制台轻易删除(加MutationObserver防篡改)
4. 独立JS模块,各页面直接引用即可
5. 不影响页面正常点击操作
6. 支持多行文字

WorkBuddy 直接给出了完整的 watermark-core.js 模块。

3.2 核心模块:watermark-core.js

整个水印引擎封装在一个 IIFE(立即执行函数)里,对外暴露三个 API:

代码语言:javascript
复制
var Watermark = (function() {
  // ... 内部实现 ...

  return {
    init: init,      // 初始化水印
    update: update,  // 动态更新文字
    destroy: destroy // 销毁水印
  };
})();
关键函数一:Canvas 生成水印图案
代码语言:javascript
复制
function createCanvasWatermark(settings) {
  var canvas = document.createElement('canvas');
  var dpr = window.devicePixelRatio || 1;    // ① 高清屏适配
  canvas.width = settings.width * dpr;
  canvas.height = settings.height * dpr;

  var ctx = canvas.getContext('2d');
  ctx.scale(dpr, dpr);                        // ② 缩放画布
  ctx.font = settings.fontSize + 'px ' + settings.fontFamily;
  ctx.fillStyle = settings.color;
  ctx.globalAlpha = settings.opacity;          // ③ 全局透明度
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';

  var lines = settings.text.split('\n');      // ④ 支持多行文字

  ctx.save();
  ctx.translate(cx, cy);
  ctx.rotate((settings.rotate * Math.PI) / 180); // ⑤ 旋转文字
  lines.forEach(function(line, idx) {
    ctx.fillText(line, 0, startY + idx * lineHeight);
  });
  ctx.restore();

  return canvas.toDataURL('image/png');       // ⑥ 导出为base64背景图
}

画布上写好文字 → 旋转 → 导出 base64 → 做成 background-image: repeat,铺满全屏。用 devicePixelRatio 乘系数保证在高分屏上文字不糊。

关键函数二:MutationObserver 防篡改

这是安全性的核心。普通 CSS 水印被删节点就没了,我们加一层监听:

代码语言:javascript
复制
function startObserver(settings) {
  var overlay = document.getElementById('__wm_overlay__');
  var parentNode = overlay.parentNode;

  observer = new MutationObserver(function(mutations) {
    var needRestore = false;
    mutations.forEach(function(mut) {
      // 检测水印节点被删除
      if (mut.type === 'childList') {
        var removed = Array.from(mut.removedNodes);
        if (removed.some(function(n) { return n.id === '__wm_overlay__'; })) {
          needRestore = true;
        }
      }
      // 检测水印属性被篡改
      if (mut.type === 'attributes' && mut.target.id === '__wm_overlay__') {
        needRestore = true;
      }
    });
    if (needRestore) {
      observer.disconnect();
      restoreWatermark(settings);   // 自动恢复
      startObserver(settings);      // 重新监听
    }
  });

  observer.observe(parentNode, { childList: true, attributes: true, ... });
}

有人在控制台执行 $('#__wm_overlay__').remove() 或修改 style.display='none',MutationObserver 检测到后立即重建水印层。除非他把整个 JS 线程停掉,否则删不掉。

关键函数三:自动初始化
代码语言:javascript
复制
if (typeof window !== 'undefined' && window.WATERMARK_CONFIG) {
  document.addEventListener('DOMContentLoaded', function() {
    Watermark.init(window.WATERMARK_CONFIG);
  });
}

页面上只需定义一个 window.WATERMARK_CONFIG 对象,模块就会在 DOM 就绪后自动初始化。这就是"三行接入"的秘密。

3.3 JSP 页面集成

在任意 JSP 页面底部引入即可:

jsp

代码语言:javascript
复制
<!-- ========== 1. 引入核心模块 ========== -->
<script src="/js/watermark-core.js"></script>

<!-- ========== 2. 配置水印参数 ========== -->
<script>
window.WATERMARK_CONFIG = {
  text: '<%= userName %> | <%= userDept %>\n<%= nowStr %>',
  fontSize: 14,
  opacity: 0.07,
  rotate: -22
};
</script>

就这么简单。<%= userName %> 是 JSP 表达式,从 Session 里取当前登录用户,渲染到页面时已经变成了具体文字(如 张三 | 运营部)。

完整示例见附件 merchant-list.jsp——一个完整的商户列表管理页,含导航栏、统计卡片、数据表格,水印层覆盖在所有内容之上,完全不影响操作。


四、模块化设计思路

把水印拆成独立模块有四个好处:

代码语言:javascript
复制
项目结构:
/webapp
  /js
    watermark-core.js        ← 水印引擎(通用)
  /WEB-INF
    /views
      merchant-list.jsp      ← 商户列表(引入水印)
      order-list.jsp         ← 订单列表(引入水印)
      report.jsp             ← 报表页(引入水印)

原则:业务代码不关心水印怎么画的,水印模块不关心页面上有什么。 两者通过 window.WATERMARK_CONFIG 这个约定接口通信。未来要改渲染方式(比如从 Canvas 升级到 WebGL),只改 watermark-core.js,所有页面自动生效。


五、三种方案对比 & 适用场景

除了 Canvas 方案,这个模块还预留了另外两种渲染模式的扩展空间(代码结构已支持切换):

方案

适合场景

安全性

清晰度

性能

CSS 纯样式

内部工具、非敏感页

⭐⭐

⭐⭐⭐

⭐⭐⭐

SVG 内联

需要无极缩放的水印

⭐⭐

⭐⭐⭐

⭐⭐

Canvas base64(本方案)

管理后台、敏感数据页

⭐⭐⭐

⭐⭐⭐

⭐⭐⭐

对于商户数据管理系统这类含敏感信息的后台,Canvas + MutationObserver 是最佳平衡点。


六、踩坑记录

用 WorkBuddy 生成代码的过程中,我踩了两个坑,顺手修了:

坑一:pointer-events 没生效,水印挡住了按钮点击

原因:水印 <div> 在最上层时,z-index: 9999 会导致它拦截所有鼠标事件,即使透明也点不到按钮。

解决:加水印容器的 pointer-events: none 是关键。注意这个属性会让水印本身也无法被选中,但这正是我们需要的——水印只是视觉效果,不参与交互。

坑二:高分屏水印文字模糊

原因:Canvas 默认按逻辑像素绘制,Retina 屏上一个像素点对应多个物理像素,文字边缘锯齿严重。

解决:canvas.width = width * devicePixelRatio,然后 ctx.scale(dpr, dpr) 缩放画布。这样 Canvas 内部用2倍/3倍分辨率渲染,再缩放到逻辑尺寸,文字锐利清晰。


七、扩展玩法

这套水印框架可以轻松扩展:

  1. 页面差异化水印:不同页面配不同水印文字,如订单页显示"订单数据-张三",报表页显示"财务报表-张三"
  2. 水印开关:加一个配置项 enabled: true/false,开发环境关掉水印方便调试
  3. 颜色水印:给不同角色配不同颜色水印,管理员红色、普通用户灰色,视觉上一眼分辨
  4. 对角线水印:改 rotate 为 -45°,配合更小的间距,水印更密集更难去除

八、总结

这个 JSP 动态水印方案的完整链路:

代码语言:javascript
复制
JSP Session → 取用户信息 → 拼装水印文字 → 注入 window.WATERMARK_CONFIG
                                                    ↓
                                          watermark-core.js 读取配置
                                                    ↓
                                          Canvas 渲染 → base64 背景图
                                                    ↓
                                          MutationObserver 防篡改

整件事 WorkBuddy 帮我干了:方案选型 + Canvas 绘图代码 + Observer 机制 + 模块封装。我只需要在 JSP 页面底部加三行 <script> 标签,把 Session 数据传给水印模块。

如果你也有 JSP 管理后台需要加水印,直接把附件里的 watermark-core.js 拷进项目,按上面的 JSP 集成方式写好配置就能用。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、一个真实的痛点
  • 二、技术选型:为什么用 Canvas?
  • 三、分步实操
    • 3.1 向 WorkBuddy 描述需求
    • 3.2 核心模块:watermark-core.js
      • 关键函数一:Canvas 生成水印图案
      • 关键函数二:MutationObserver 防篡改
      • 关键函数三:自动初始化
    • 3.3 JSP 页面集成
  • 四、模块化设计思路
  • 五、三种方案对比 & 适用场景
  • 六、踩坑记录
  • 七、扩展玩法
  • 八、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档