前端實(shí)現(xiàn)網(wǎng)頁(yè)水印及防移除的多種方案
一、前言
在Web開(kāi)發(fā)中,保護(hù)內(nèi)容版權(quán)和防止信息泄露變得越來(lái)越重要。為網(wǎng)頁(yè)添加水印是一種常見(jiàn)的內(nèi)容保護(hù)手段,可以用于標(biāo)識(shí)內(nèi)容來(lái)源、追蹤泄露渠道或聲明版權(quán)。本文將詳細(xì)介紹前端實(shí)現(xiàn)網(wǎng)頁(yè)水印的多種方法,并探討如何防止水印被輕易移除的技術(shù)方案。
二、網(wǎng)頁(yè)水印的基本實(shí)現(xiàn)原理
網(wǎng)頁(yè)水印的基本原理是在頁(yè)面內(nèi)容之上疊加一層半透明的文字或圖片,使其不影響用戶正常瀏覽,但又能清晰可見(jiàn)。實(shí)現(xiàn)方式主要包括:
- Canvas繪制水印
- CSS背景圖水印
- SVG水印
- DOM元素覆蓋水印
三、基礎(chǔ)水印實(shí)現(xiàn)方案
3.1 Canvas繪制水印
Canvas是HTML5提供的繪圖API,非常適合用于生成動(dòng)態(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('機(jī)密文檔 嚴(yán)禁外傳'); 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屬性可以輕松實(shí)現(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)">機(jī)密文檔 嚴(yán)禁外傳</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('機(jī)密文檔 嚴(yán)禁外傳'); 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ǔ)水印容易被開(kāi)發(fā)者工具移除,我們需要增加防護(hù)措施。
4.1 防刪除機(jī)制
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); // 防止控制臺(tái)修改樣式 Object.defineProperty(watermark.style, 'display', { set: function() {}, get: function() { return 'block'; } }); }
4.2 防隱藏機(jī)制
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 動(dòng)態(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('機(jī)密文檔 嚴(yán)禁外傳', 50, 100); // 添加用戶特定信息 ctx.font = '12px Arial'; ctx.fillText(`用戶: ${userInfo.name}`, 50, 130); ctx.fillText(`時(shí)間: ${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ù)端配合的水印方案
前端水印容易被繞過(guò),結(jié)合服務(wù)端可以增強(qiáng)安全性。
// 前端代碼 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('機(jī)密文檔 嚴(yán)禁外傳', 50, 100); ctx.fillText(`用戶ID: ${userId}`, 50, 130); ctx.fillText(`時(shí)間: ${new Date().toISOString()}`, 50, 150); // 返回圖片 const buffer = canvas.toBuffer('image/png'); res.set('Content-Type', 'image/png'); res.send(buffer); });
五、高級(jí)防護(hù)方案
5.1 屏幕錄制防護(hù)
function preventScreenCapture() { // 檢測(cè)常見(jiàn)錄屏軟件 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>檢測(cè)到屏幕錄制軟件,內(nèi)容保護(hù)已啟用</h1>'; return true; } } return false; }; // 檢測(cè)DevTools開(kāi)啟 const detectDevTools = () => { const devtools = /./; devtools.toString = function() { document.body.innerHTML = '<h1>開(kāi)發(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ù)字水印將信息隱藏在視覺(jué)不可見(jiàn)的像素中。
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)換為二進(jìn)制 const binaryText = watermarkText.split('').map(char => char.charCodeAt(0).toString(2).padStart(8, '0')).join(''); // 在每個(gè)像素的最低有效位嵌入1位信息 for (let i = 0; i < binaryText.length; i++) { const pixelIndex = i * 4; if (pixelIndex >= data.length) break; // 修改藍(lán)色通道的最低有效位 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; // 提取藍(lán)色通道的最低有效位 binaryText += (data[pixelIndex + 2] & 1).toString(); } // 將二進(jìn)制轉(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; }
六、水印實(shí)現(xiàn)流程圖
七、最佳實(shí)踐建議
- 多層防護(hù):結(jié)合多種水印技術(shù),增加移除難度
- 用戶特定信息:在動(dòng)態(tài)水印中包含用戶ID、時(shí)間等信息,便于追蹤
- 性能考慮:避免過(guò)于復(fù)雜的水印影響頁(yè)面性能
- 響應(yīng)式設(shè)計(jì):確保水印在不同屏幕尺寸下都能正常顯示
- 法律合規(guī):確保水印使用符合當(dāng)?shù)胤煞ㄒ?guī)
八、總結(jié)
網(wǎng)頁(yè)水印的實(shí)現(xiàn)和防護(hù)是一個(gè)不斷演進(jìn)的技術(shù)領(lǐng)域。本文介紹了從基礎(chǔ)到高級(jí)的多種水印實(shí)現(xiàn)方案,以及相應(yīng)的防移除技術(shù)。需要注意的是,沒(méi)有任何前端水印方案是絕對(duì)安全的,但通過(guò)組合多種技術(shù)可以顯著提高移除難度。對(duì)于高安全性要求的場(chǎng)景,建議結(jié)合服務(wù)端驗(yàn)證和數(shù)字水印等高級(jí)方案。
在實(shí)際應(yīng)用中,應(yīng)根據(jù)具體需求選擇合適的水印方案,平衡安全性、用戶體驗(yàn)和性能開(kāi)銷。隨著Web技術(shù)的不斷發(fā)展,水印技術(shù)也將持續(xù)演進(jìn),開(kāi)發(fā)者需要保持對(duì)新技術(shù)的學(xué)習(xí)和關(guān)注。
以上就是前端實(shí)現(xiàn)網(wǎng)頁(yè)水印及防移除的多種方案的詳細(xì)內(nèi)容,更多關(guān)于前端網(wǎng)頁(yè)水印及防移除的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript 實(shí)現(xiàn)同時(shí)選取多個(gè)時(shí)間段的方法
這篇文章主要介紹了JavaScript 實(shí)現(xiàn)同時(shí)選取多個(gè)時(shí)間段的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10js實(shí)現(xiàn)符合國(guó)情的日期插件詳解
本篇文章主要介紹了js實(shí)現(xiàn)符合國(guó)情的日期插件的方法與步驟。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01bootstrap精簡(jiǎn)教程_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了bootstrap精簡(jiǎn)教程,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)版簡(jiǎn)易計(jì)算器功能
這篇文章主要介紹了JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)版簡(jiǎn)易計(jì)算器功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07js實(shí)現(xiàn)手機(jī)發(fā)送驗(yàn)證碼功能
本文主要介紹了js實(shí)現(xiàn)手機(jī)發(fā)送驗(yàn)證碼功能的示例。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03fw.qq.com/ipaddress已失效 javascript獲得客戶端IP的新方法
一直以來(lái),我都是通過(guò)http://fw.qq.com/ipaddress來(lái)獲得客戶端用戶的IP,這個(gè)方法簡(jiǎn)單、快速、實(shí)用2012-01-01js中字符替換函數(shù)String.replace()使用技巧
js中字符替換函數(shù)String.replace()使用技巧,字符替換經(jīng)常用的到。2011-08-08