一、基础防护措施
1.css禁用文本选择
.protected-content {
-webkit-user-select: none; /* Chrome, Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE 10+ */
user-select: none; /* 标准语法 */
}优点:
实现简单,一行代码搞定
不影响页面性能
加载速度快
缺点:
懂技术的用户通过开发者工具就能破解
用户无法选择需要的文本
体验不太好
2.JavaScript事件拦截
// 禁止复制
document.addEventListener('copy', function(e) {
e.preventDefault();
alert('抱歉,此内容受保护,不允许复制');
});
// 禁止右键菜单
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
});
// 禁止快捷键
document.addEventListener('keydown', function(e) {
// 禁止 Ctrl+C
if (e.ctrlKey && e.key === 'c') {
e.preventDefault();
}
// 禁止 Ctrl+A
if (e.ctrlKey && e.key === 'a') {
e.preventDefault();
}
});优点:
防护比较全面
可以自定义提示信息
覆盖多种复制方式
缺点:
用户禁用JavaScript就失效了
严重影响正常使用
可能干扰浏览器功能
二、进阶防护方案
1.添加复制水印
document.addEventListener('copy', function(e) {
const selectedText = window.getSelection().toString();
if (selectedText) {
const watermark = '\n\n—— 本文内容来自XXX网站,转载请保留此声明 ——';
e.clipboardData.setData('text/plain', selectedText + watermark);
e.preventDefault();
alert('内容已复制,请保留版权信息');
}
});优点:
不阻止用户复制,体验较好
自动添加版权信息
可以作为维权证据
缺点:
不能阻止内容传播
懂技术的用户可以去掉水印
2.动态加载内容
classContentProtector{
constructor(contentId) {
this.contentElement = document.getElementById(contentId);
this.contentParts = [];
this.currentIndex = 0;
}
// 把内容分割成多个部分
setContent(text) {
this.contentParts = this.splitText(text, 50); // 每50个字符一段
this.renderNextPart();
}
// 分段显示
renderNextPart() {
if (this.currentIndex < this.contentParts.length) {
this.contentElement.innerhtml += this.contentParts[this.currentIndex];
this.currentIndex++;
// 随机延迟加载下一段
setTimeout(() => {
this.renderNextPart();
}, Math.random() * 500 + 200);
}
}
splitText(text, chunkSize) {
const chunks = [];
for (let i = 0; i < text.length; i += chunkSize) {
chunks.push(text.slice(i, i + chunkSize));
}
return chunks;
}
}
// 使用示例
const protector = new ContentProtector('article-content');
protector.setContent('这是一篇很长的文章内容...');3.Canvas渲染文字
classCanvasTextRenderer{
constructor(containerId) {
this.container = document.getElementById(containerId);
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
this.setupCanvas();
}
setupCanvas() {
// 设置Canvas大小
this.canvas.width = this.container.clientWidth;
this.canvas.height = 800;
this.canvas.style.width = '100%';
this.canvas.style.border = '1px solid #eee';
// 设置文字样式
this.ctx.font = '16px Arial';
this.ctx.fillStyle = '#333';
this.ctx.textBaseline = 'top';
this.container.appendChild(this.canvas);
}
renderText(text) {
const lines = this.wrapText(text, this.canvas.width - 40);
let y = 20;
lines.forEach(line => {
this.ctx.fillText(line, 20, y);
y += 24; // 行高
});
}
wrapText(text, maxWidth) {
const words = text.split(' ');
const lines = [];
let currentLine = words[0];
for (let i = 1; i < words.length; i++) {
const word = words[i];
const width = this.ctx.measureText(currentLine + ' ' + word).width;
if (width < maxWidth) {
currentLine += ' ' + word;
} else {
lines.push(currentLine);
currentLine = word;
}
}
lines.push(currentLine);
return lines;
}
}
// 使用
const renderer = new CanvasTextRenderer('content-container');
renderer.renderText('这是要保护的文本内容...');4.文字转图片
functiontextToImage(text, options = {}) {
returnnewPromise((resolve) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 设置Canvas
canvas.width = options.width || 600;
canvas.height = options.height || 400;
// 设置样式
ctx.fillStyle = options.backgroundColor || '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.font = options.font || '16px Arial';
ctx.fillStyle = options.color || '#000000';
ctx.textBaseline = 'top';
// 处理文字换行
const lines = [];
const maxWidth = canvas.width - 40;
let currentLine = '';
text.split('').forEach(char => {
const testLine = currentLine + char;
const metrics = ctx.measureText(testLine);
if (metrics.width > maxWidth) {
lines.push(currentLine);
currentLine = char;
} else {
currentLine = testLine;
}
});
lines.push(currentLine);
// 绘制文字
lines.forEach((line, index) => {
ctx.fillText(line, 20, 20 + index * 24);
});
// 添加水印
if (options.watermark) {
ctx.fillStyle = 'rgba(0,0,0,0.1)';
ctx.font = '12px Arial';
ctx.fillText(options.watermark, 20, canvas.height - 20);
}
resolve(canvas.toDataURL('image/png'));
});
}
// 使用
textToImage('这是要保护的文本内容...', {
watermark: '版权保护内容'
}).then(dataUrl => {
document.getElementById('content').innerHTML =
`<img src="${dataUrl}" alt="保护内容">`;
});三、综合防护方案
classComprehensiveProtector {
constructor(contentElement) {
this.element = contentElement;
this.setupProtection();
}
setupProtection() {
//1. 禁用选择
this.element.style.userSelect = 'none';
//2. 事件监听
this.bindEvents();
//3. 定期检查DOM
this.startDOMCheck();
}
bindEvents() {
// 复制保护
this.element.addEventListener('copy', (e) => {
e.preventDefault();
this.showMessage('内容受保护,如需引用请联系授权');
});
// 右键菜单保护
this.element.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
// 键盘保护
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && (e.key === 'c' || e.key === 'a')) {
e.preventDefault();
}
});
}
startDOMCheck() {
// 定期检查是否被开发者工具修改
setInterval(() => {
const originalContent = this.element.getAttribute('>);
if (this.element.innerHTML !== originalContent) {
this.element.innerHTML = originalContent;
this.showMessage('检测到异常操作,内容已重置');
}
}, 1000);
}
showMessage(text) {
const msg = document.createElement('div');
msg.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: #ff4757;
color: white;
padding: 10px 20px;
border-radius: 4px;
z-index: 10000;
`;
msg.textContent = text;
document.body.appendChild(msg);
setTimeout(() => {
document.body.removeChild(msg);
}, 3000);
}
}
// 初始化保护
const protector = new ComprehensiveProtector(document.getElementById('protected-content'));四、不同方案对比
五、总结
技术限制
要明白一个事实:完全防止复制在技术上是不可能的。有经验的用户总能找到办法。
平衡点
在内容保护和用户体验之间找到平衡。过度防护可能赶走正常用户。
商业思考
有时候,适度的开放反而能带来更多用户。想想看,你的内容真的需要这么严格的保护吗?
最终建议
普通内容用CSS + 水印就够了
重要内容可以加事件拦截
核心内容考虑Canvas或图片方案
始终把用户体验放在重要位置
记住,最好的保护是提供独特的价值,让用户愿意为你的内容付费,而不是想办法绕过保护。
原创
防复制实战技巧!网页内容无法被轻易复制
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
评论交流
欢迎留下你的想法