前端實現(xiàn)網(wǎng)頁水印及防移除的多種方案
一、前言
在Web開發(fā)中,保護內(nèi)容版權(quán)和防止信息泄露變得越來越重要。為網(wǎng)頁添加水印是一種常見的內(nèi)容保護手段,可以用于標(biāo)識內(nèi)容來源、追蹤泄露渠道或聲明版權(quán)。本文將詳細介紹前端實現(xiàn)網(wǎng)頁水印的多種方法,并探討如何防止水印被輕易移除的技術(shù)方案。
二、網(wǎng)頁水印的基本實現(xiàn)原理
網(wǎng)頁水印的基本原理是在頁面內(nèi)容之上疊加一層半透明的文字或圖片,使其不影響用戶正常瀏覽,但又能清晰可見。實現(xiàn)方式主要包括:
- Canvas繪制水印
- CSS背景圖水印
- SVG水印
- DOM元素覆蓋水印
三、基礎(chǔ)水印實現(xiàn)方案
3.1 Canvas繪制水印
Canvas是HTML5提供的繪圖API,非常適合用于生成動態(tài)水印。
function createWatermark(text) {
const canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 200;
const ctx = canvas.getContext('2d');
ctx.font = '16px Arial';
ctx.fillStyle = 'rgba(200, 200, 200, 0.3)';
ctx.rotate(-20 * Math.PI / 180);
ctx.fillText(text, 50, 100);
return canvas.toDataURL('image/png');
}
function applyWatermark() {
const watermarkUrl = createWatermark('機密文檔 嚴禁外傳');
const watermarkDiv = document.createElement('div');
watermarkDiv.style.position = 'fixed';
watermarkDiv.style.top = '0';
watermarkDiv.style.left = '0';
watermarkDiv.style.width = '100%';
watermarkDiv.style.height = '100%';
watermarkDiv.style.pointerEvents = 'none';
watermarkDiv.style.backgroundImage = `url(${watermarkUrl})`;
watermarkDiv.style.zIndex = '9999';
document.body.appendChild(watermarkDiv);
}
window.onload = applyWatermark;
3.2 CSS背景圖水印
使用CSS的background-image屬性可以輕松實現(xiàn)全屏水印效果。
body {
position: relative;
}
body::after {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
pointer-events: none;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200"><text x="50" y="100" font-family="Arial" font-size="16" fill="rgba(200,200,200,0.3)" transform="rotate(-20)">機密文檔 嚴禁外傳</text></svg>');
background-repeat: repeat;
}
3.3 SVG水印
SVG水印具有矢量特性,縮放不失真。
function createSvgWatermark(text) {
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200">
<text x="50" y="100" font-family="Arial" font-size="16"
fill="rgba(200,200,200,0.3)" transform="rotate(-20)">
${text}
</text>
</svg>`;
return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
}
function applySvgWatermark() {
const watermarkUrl = createSvgWatermark('機密文檔 嚴禁外傳');
const style = document.createElement('style');
style.innerHTML = `
body::after {
content: "";
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
pointer-events: none;
background-image: url("${watermarkUrl}");
background-repeat: repeat;
}
`;
document.head.appendChild(style);
}
3.4 DOM元素覆蓋水印
使用大量小DOM元素平鋪形成水印,增加移除難度。
function createDomWatermark(text) {
const container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '0';
container.style.left = '0';
container.style.width = '100%';
container.style.height = '100%';
container.style.zIndex = '9999';
container.style.pointerEvents = 'none';
container.style.overflow = 'hidden';
for (let i = 0; i < 200; i++) {
const watermark = document.createElement('div');
watermark.textContent = text;
watermark.style.position = 'absolute';
watermark.style.color = 'rgba(200, 200, 200, 0.3)';
watermark.style.fontSize = '16px';
watermark.style.transform = 'rotate(-20deg)';
watermark.style.userSelect = 'none';
const x = Math.random() * window.innerWidth;
const y = Math.random() * window.innerHeight;
watermark.style.left = `${x}px`;
watermark.style.top = `${y}px`;
container.appendChild(watermark);
}
document.body.appendChild(container);
}
四、水印防移除技術(shù)
基礎(chǔ)水印容易被開發(fā)者工具移除,我們需要增加防護措施。
4.1 防刪除機制
function protectWatermark() {
const watermark = document.getElementById('watermark');
// 監(jiān)控DOM變化
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.removedNodes.forEach((node) => {
if (node === watermark) {
document.body.appendChild(watermark);
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// 定期檢查水印是否存在
setInterval(() => {
if (!document.contains(watermark)) {
document.body.appendChild(watermark);
}
}, 1000);
// 防止控制臺修改樣式
Object.defineProperty(watermark.style, 'display', {
set: function() {},
get: function() { return 'block'; }
});
}
4.2 防隱藏機制
function preventHideWatermark() {
const watermark = document.getElementById('watermark');
// 監(jiān)控樣式變化
const styleObserver = new MutationObserver(() => {
if (watermark.style.display === 'none' ||
watermark.style.visibility === 'hidden' ||
watermark.style.opacity === '0') {
watermark.style.display = 'block';
watermark.style.visibility = 'visible';
watermark.style.opacity = '1';
}
});
styleObserver.observe(watermark, {
attributes: true,
attributeFilter: ['style']
});
// 防止透明度被修改
Object.defineProperty(watermark.style, 'opacity', {
set: function(value) {
if (parseFloat(value) < 0.1) {
this._opacity = '0.3';
} else {
this._opacity = value;
}
},
get: function() {
return this._opacity || '0.3';
}
});
}
4.3 動態(tài)水印技術(shù)
function dynamicWatermark(userInfo) {
const canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 200;
const ctx = canvas.getContext('2d');
// 繪制基礎(chǔ)水印
ctx.font = '16px Arial';
ctx.fillStyle = 'rgba(200, 200, 200, 0.3)';
ctx.rotate(-20 * Math.PI / 180);
ctx.fillText('機密文檔 嚴禁外傳', 50, 100);
// 添加用戶特定信息
ctx.font = '12px Arial';
ctx.fillText(`用戶: ${userInfo.name}`, 50, 130);
ctx.fillText(`時間: ${new Date().toLocaleString()}`, 50, 150);
// 應(yīng)用水印
const watermarkDiv = document.createElement('div');
watermarkDiv.id = 'watermark';
watermarkDiv.style.position = 'fixed';
watermarkDiv.style.top = '0';
watermarkDiv.style.left = '0';
watermarkDiv.style.width = '100%';
watermarkDiv.style.height = '100%';
watermarkDiv.style.pointerEvents = 'none';
watermarkDiv.style.backgroundImage = `url(${canvas.toDataURL('image/png')})`;
watermarkDiv.style.zIndex = '9999';
document.body.appendChild(watermarkDiv);
// 定期更新水印
setInterval(() => {
dynamicWatermark(userInfo);
}, 60000); // 每分鐘更新一次
}
4.4 服務(wù)端配合的水印方案
前端水印容易被繞過,結(jié)合服務(wù)端可以增強安全性。
// 前端代碼
async function getWatermarkImage(userId) {
const response = await fetch(`/api/watermark?userId=${userId}`);
const blob = await response.blob();
return URL.createObjectURL(blob);
}
async function applyServerWatermark() {
const userId = getCurrentUserId(); // 獲取當(dāng)前用戶ID
const watermarkUrl = await getWatermarkImage(userId);
const watermarkDiv = document.createElement('div');
watermarkDiv.style.position = 'fixed';
watermarkDiv.style.top = '0';
watermarkDiv.style.left = '0';
watermarkDiv.style.width = '100%';
watermarkDiv.style.height = '100%';
watermarkDiv.style.pointerEvents = 'none';
watermarkDiv.style.backgroundImage = `url(${watermarkUrl})`;
watermarkDiv.style.zIndex = '9999';
document.body.appendChild(watermarkDiv);
}
// 服務(wù)端示例(Node.js)
app.get('/api/watermark', (req, res) => {
const { userId } = req.query;
const canvas = createCanvas(300, 200);
const ctx = canvas.getContext('2d');
// 繪制水印
ctx.font = '16px Arial';
ctx.fillStyle = 'rgba(200, 200, 200, 0.3)';
ctx.rotate(-20 * Math.PI / 180);
ctx.fillText('機密文檔 嚴禁外傳', 50, 100);
ctx.fillText(`用戶ID: ${userId}`, 50, 130);
ctx.fillText(`時間: ${new Date().toISOString()}`, 50, 150);
// 返回圖片
const buffer = canvas.toBuffer('image/png');
res.set('Content-Type', 'image/png');
res.send(buffer);
});
五、高級防護方案
5.1 屏幕錄制防護
function preventScreenCapture() {
// 檢測常見錄屏軟件
const detectScreenCapture = () => {
const perf = window.performance || window.webkitPerformance;
const entries = perf?.getEntries() || [];
const screenCaptureApps = ['bandicam', 'obs', 'camtasia', 'screencast'];
for (const entry of entries) {
if (screenCaptureApps.some(app => entry.name.toLowerCase().includes(app))) {
document.body.innerHTML = '<h1>檢測到屏幕錄制軟件,內(nèi)容保護已啟用</h1>';
return true;
}
}
return false;
};
// 檢測DevTools開啟
const detectDevTools = () => {
const devtools = /./;
devtools.toString = function() {
document.body.innerHTML = '<h1>開發(fā)者工具已禁用</h1>';
return '';
};
console.log('%c', devtools);
};
// 定期檢查
setInterval(() => {
detectScreenCapture();
detectDevTools();
}, 1000);
// 禁止右鍵菜單和快捷鍵
document.addEventListener('contextmenu', e => e.preventDefault());
document.addEventListener('keydown', e => {
if (e.ctrlKey && (e.key === 'u' || e.key === 's' || e.key === 'c')) {
e.preventDefault();
}
if (e.key === 'F12' || (e.ctrlKey && e.shiftKey && e.key === 'I')) {
e.preventDefault();
}
});
}
5.2 數(shù)字水印技術(shù)
數(shù)字水印將信息隱藏在視覺不可見的像素中。
function embedDigitalWatermark(imageElement, watermarkText) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = imageElement.width;
canvas.height = imageElement.height;
ctx.drawImage(imageElement, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// 將文本轉(zhuǎn)換為二進制
const binaryText = watermarkText.split('').map(char =>
char.charCodeAt(0).toString(2).padStart(8, '0')).join('');
// 在每個像素的最低有效位嵌入1位信息
for (let i = 0; i < binaryText.length; i++) {
const pixelIndex = i * 4;
if (pixelIndex >= data.length) break;
// 修改藍色通道的最低有效位
data[pixelIndex + 2] = (data[pixelIndex + 2] & 0xFE) | parseInt(binaryText[i]);
}
ctx.putImageData(imageData, 0, 0);
imageElement.src = canvas.toDataURL('image/png');
}
// 提取數(shù)字水印
function extractDigitalWatermark(imageElement, length) {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = imageElement.width;
canvas.height = imageElement.height;
ctx.drawImage(imageElement, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
let binaryText = '';
for (let i = 0; i < length * 8; i++) {
const pixelIndex = i * 4;
if (pixelIndex >= data.length) break;
// 提取藍色通道的最低有效位
binaryText += (data[pixelIndex + 2] & 1).toString();
}
// 將二進制轉(zhuǎn)換為文本
let text = '';
for (let i = 0; i < binaryText.length; i += 8) {
const byte = binaryText.substr(i, 8);
text += String.fromCharCode(parseInt(byte, 2));
}
return text;
}
六、水印實現(xiàn)流程圖

七、最佳實踐建議
- 多層防護:結(jié)合多種水印技術(shù),增加移除難度
- 用戶特定信息:在動態(tài)水印中包含用戶ID、時間等信息,便于追蹤
- 性能考慮:避免過于復(fù)雜的水印影響頁面性能
- 響應(yīng)式設(shè)計:確保水印在不同屏幕尺寸下都能正常顯示
- 法律合規(guī):確保水印使用符合當(dāng)?shù)胤煞ㄒ?guī)
八、總結(jié)
網(wǎng)頁水印的實現(xiàn)和防護是一個不斷演進的技術(shù)領(lǐng)域。本文介紹了從基礎(chǔ)到高級的多種水印實現(xiàn)方案,以及相應(yīng)的防移除技術(shù)。需要注意的是,沒有任何前端水印方案是絕對安全的,但通過組合多種技術(shù)可以顯著提高移除難度。對于高安全性要求的場景,建議結(jié)合服務(wù)端驗證和數(shù)字水印等高級方案。
在實際應(yīng)用中,應(yīng)根據(jù)具體需求選擇合適的水印方案,平衡安全性、用戶體驗和性能開銷。隨著Web技術(shù)的不斷發(fā)展,水印技術(shù)也將持續(xù)演進,開發(fā)者需要保持對新技術(shù)的學(xué)習(xí)和關(guān)注。
以上就是前端實現(xiàn)網(wǎng)頁水印及防移除的多種方案的詳細內(nèi)容,更多關(guān)于前端網(wǎng)頁水印及防移除的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript 實現(xiàn)同時選取多個時間段的方法
這篇文章主要介紹了JavaScript 實現(xiàn)同時選取多個時間段的方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
bootstrap精簡教程_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了bootstrap精簡教程,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
JavaScript實現(xiàn)網(wǎng)頁版簡易計算器功能
這篇文章主要介紹了JavaScript實現(xiàn)網(wǎng)頁版簡易計算器功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07
fw.qq.com/ipaddress已失效 javascript獲得客戶端IP的新方法
一直以來,我都是通過http://fw.qq.com/ipaddress來獲得客戶端用戶的IP,這個方法簡單、快速、實用2012-01-01
js中字符替換函數(shù)String.replace()使用技巧
js中字符替換函數(shù)String.replace()使用技巧,字符替換經(jīng)常用的到。2011-08-08

