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();

总结

 
  1. 优先选择前端 JS 方案:无后端依赖、性能高、支持动态内容,适合绝大多数 Web 页面水印场景。
  2. 后端生成水印图片:适合需要严格控制水印样式、防止前端篡改的场景,通过 Java 生成 Base64 图片返回前端使用。
  3. PDF 水印用 iText:针对文档下载场景,后端直接生成带水印的 PDF 文件。
 
核心关键点:前端水印通过 canvas 生成 base64 背景图平铺实现,后端水印可生成图片或直接处理 PDF,根据实际业务场景选择即可。
posted @ 2026-02-09 08:58  ConfidentLiu  阅读(87)  评论(0)    收藏  举报