web应用水印实现方案
为 Web 项目添加水印主要有前端纯 JS 实现(最常用)、后端生成水印图片、后端 PDF 水印(针对文档下载) 三种主流方案,以下是详细实现:
方案一:前端 JS 动态生成水印(推荐,无后端依赖)
这是最常用的方案,直接在浏览器端通过 JS 生成水印,性能好、不占用后端资源,支持动态内容(如用户名、时间)。
实现代码(通用版):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Web水印示例</title>
<style>
/* 水印容器样式,固定定位覆盖整个页面 */
.watermark-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* 不影响页面元素交互 */
z-index: 9999; /* 确保水印在最上层 */
opacity: 0.15; /* 水印透明度,可调整 */
}
</style>
</head>
<body>
<h1>Java Web项目水印示例</h1>
<script>
/**
* 生成页面水印
* @param {String} text 水印文字(如用户名、公司名称)
* @param {Object} options 配置项(可选)
*/
function createWatermark(text = "默认水印", options = {}) {
// 默认配置
const config = {
fontSize: options.fontSize || 16, // 字体大小
fontFamily: options.fontFamily || "Microsoft Yahei", // 字体
rotate: options.rotate || -20, // 旋转角度(负数向左旋转)
color: options.color || "#000000", // 字体颜色
spacing: options.spacing || 100, // 水印间距
};
// 创建canvas元素生成水印
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
// 设置canvas尺寸(单个水印单元)
canvas.width = config.spacing;
canvas.height = config.spacing;
// 绘制水印文字
ctx.font = `${config.fontSize}px ${config.fontFamily}`;
ctx.fillStyle = config.color;
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.rotate((config.rotate * Math.PI) / 180); // 角度转弧度
ctx.fillText(text, canvas.width / 2, canvas.height / 2);
// 创建水印容器并添加到页面
let watermark = document.querySelector(".watermark-container");
if (!watermark) {
watermark = document.createElement("div");
watermark.className = "watermark-container";
document.body.appendChild(watermark);
}
// 设置水印背景(重复平铺)
watermark.style.backgroundImage = `url(${canvas.toDataURL("image/png")})`;
}
// ========== 调用示例 ==========
// 1. 固定文字水印
createWatermark("XX公司 2026-02-09");
// 2. 动态获取后端传递的用户信息(Java后端通过模板渲染传递)
// const userName = [[${user.name}]]; // Freemarker/Thymeleaf模板语法
// createWatermark(`${userName} 禁止复制`, { fontSize: 14, rotate: -15 });
</script>
</body>
</html>
关键说明:
pointer-events: none确保水印不遮挡页面元素的点击 / 输入事件canvas.toDataURL()将画布转为 base64 图片,作为背景平铺实现水印效果- 可通过 Java 模板引擎(Thymeleaf/Freemarker)将后端的用户信息、时间等动态传递到前端
方案二:后端生成水印图片(Java 实现)
适合需要统一水印样式、前端无法修改的场景,后端生成水印图片后返回给前端使用。
Java 后端代码(生成水印图片):
import javax.imageio.ImageIO; import java.awt.*; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Base64; /** * Java后端生成水印图片工具类 */ public class WatermarkUtils { /** * 生成水印图片并返回Base64编码 * @param text 水印文字 * @param width 水印单元宽度 * @param height 水印单元高度 * @return Base64编码的图片字符串 */ public static String generateWatermarkImage(String text, int width, int height) { // 创建画布 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = image.createGraphics(); // 设置透明背景 image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT); g2d.dispose(); g2d = image.createGraphics(); // 设置水印样式 g2d.setColor(new Color(0, 0, 0, 38)); // 黑色,透明度15%(0-255) g2d.setFont(new Font("微软雅黑", Font.PLAIN, 16)); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); // 旋转文字 g2d.rotate(Math.toRadians(-20), width / 2, height / 2); // 计算文字位置(居中) FontRenderContext frc = g2d.getFontRenderContext(); AffineTransform affinetransform = new AffineTransform(); Font font = g2d.getFont(); java.awt.font.TextLayout textLayout = new java.awt.font.TextLayout(text, font, frc); float textWidth = textLayout.getAdvance(); float textHeight = textLayout.getAscent() - textLayout.getDescent(); int x = (width - (int) textWidth) / 2; int y = (height + (int) textHeight) / 2; // 绘制文字 g2d.drawString(text, x, y); g2d.dispose(); // 转为Base64 try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { ImageIO.write(image, "png", baos); return "data:image/png;base64," + Base64.getEncoder().encodeToString(baos.toByteArray()); } catch (IOException e) { throw new RuntimeException("生成水印图片失败", e); } } // 测试示例 public static void main(String[] args) { String watermarkBase64 = generateWatermarkImage("XX公司 内部文档", 200, 200); System.out.println(watermarkBase64); // 前端可直接使用该Base64作为背景图 } }
前端使用方式:
// 从Java后端接口获取水印图片Base64 fetch("/api/watermark") .then(res => res.json()) .then(data => { const watermark = document.createElement("div"); watermark.className = "watermark-container"; watermark.style.backgroundImage = `url(${data.base64})`; document.body.appendChild(watermark); });
方案三:PDF 文档水印(后端生成)
如果你的 Web 项目需要为下载的 PDF 文档添加水印,可使用 iText 库实现:
Maven 依赖:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.2.5</version>
</dependency>
Java 实现代码:
import com.itextpdf.kernel.colors.ColorConstants; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.properties.TextAlignment; import com.itextpdf.layout.properties.VerticalAlignment; import java.io.File; import java.io.IOException; public class PdfWatermarkUtils { public static void addPdfWatermark(String srcPath, String destPath, String watermarkText) throws IOException { PdfDocument pdfDoc = new PdfDocument(new PdfReader(srcPath), new PdfWriter(destPath)); Document document = new Document(pdfDoc); int pageCount = pdfDoc.getNumberOfPages(); // 为每一页添加水印 for (int i = 1; i <= pageCount; i++) { Paragraph watermark = new Paragraph(watermarkText) .setFontSize(30) .setFontColor(ColorConstants.BLACK, 0.15f) // 透明度15% .setRotationAngle(Math.toRadians(-20)); // 旋转角度 // 将水印添加到页面居中位置 document.showTextAligned(watermark, pdfDoc.getPage(i).getPageSize().getWidth() / 2, pdfDoc.getPageSize().getHeight() / 2, i, TextAlignment.CENTER, VerticalAlignment.MIDDLE, Math.toRadians(-20)); } document.close(); } // 测试示例 public static void main(String[] args) throws IOException { addPdfWatermark("source.pdf", "target.pdf", "XX公司 内部文档 2026-02-09"); } }
增强建议(防篡改 / 防删除)
为了防止用户通过浏览器调试工具删除水印,可添加以下增强措施:
// 监听水印元素是否被删除,自动重新生成 function monitorWatermark() { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.removedNodes.length > 0) { for (let node of mutation.removedNodes) { if (node.className === "watermark-container") { createWatermark("XX公司 2026-02-09"); // 重新生成水印 break; } } } }); }); // 监听body的子节点变化 observer.observe(document.body, { childList: true, subtree: true }); } // 调用监控函数 monitorWatermark();
总结
- 优先选择前端 JS 方案:无后端依赖、性能高、支持动态内容,适合绝大多数 Web 页面水印场景。
- 后端生成水印图片:适合需要严格控制水印样式、防止前端篡改的场景,通过 Java 生成 Base64 图片返回前端使用。
- PDF 水印用 iText:针对文档下载场景,后端直接生成带水印的 PDF 文件。
核心关键点:前端水印通过 canvas 生成 base64 背景图平铺实现,后端水印可生成图片或直接处理 PDF,根据实际业务场景选择即可。

浙公网安备 33010602011771号