在前端开发中,出于安全和隐私的考虑,浏览器不允许直接获取硬件的唯一标识(如 MAC 地址、CPU 序列号等)。但可以通过以下方法生成设备指纹(Device Fingerprint),近似实现设备唯一标识:
1. 组合浏览器特性生成指纹
通过收集浏览器和系统的多种信息,生成唯一性较高的字符串:
const fingerprint = {
userAgent: navigator.userAgent,
language: navigator.language,
screen: {
width: screen.width,
height: screen.height,
colorDepth: screen.colorDepth,
},
timezone: new Date().getTimezoneOffset(),
plugins: Array.from(navigator.plugins).map(p => p.name).join(','),
webglVendor: (() => {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
return gl?.getParameter(gl.VENDOR);
})(),
fonts: (() => {
const fonts = [];
const testString = "abcdefghijklmnopqrstuvwxyz";
const testFont = "Comic Sans MS";
const ctx = document.createElement('canvas').getContext('2d');
ctx.font = '72px monospace';
const defaultWidth = ctx.measureText(testString).width;
ctx.font = `72px ${testFont}, monospace`;
if (ctx.measureText(testString).width !== defaultWidth) {
fonts.push(testFont);
}
return fonts;
})(),
};
// 生成哈希(例如使用 SHA-256)
const hash = async (data) => {
const encoder = new TextEncoder();
const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(JSON.stringify(data)));
return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join('');
};
hash(fingerprint).then(console.log); // 输出设备指纹
2. 使用第三方库
一些开源库(如 FingerprintJS
)封装了更复杂的指纹生成逻辑:
# 安装 FingerprintJS
npm install @fingerprintjs/fingerprintjs
import FingerprintJS from '@fingerprintjs/fingerprintjs';
(async () => {
const fp = await FingerprintJS.load();
const result = await fp.get();
console.log(result.visitorId); // 生成的设备指纹
})();
3. Canvas 指纹
通过 Canvas 渲染的微小差异生成唯一标识:
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#069';
ctx.fillText('Hello, Fingerprint', 2, 15);
return canvas.toDataURL();
}
4. WebRTC 泄露本地 IP
(注意:部分浏览器已限制此行为)
const getLocalIP = (callback) => {
const pc = new RTCPeerConnection({ iceServers: [] });
pc.createDataChannel('');
pc.createOffer().then(offer => pc.setLocalDescription(offer));
pc.onicecandidate = (ice) => {
if (ice.candidate?.candidate?.match(/(\d+\.\d+\.\d+\.\d+)/)) {
callback(RegExp.$1);
}
};
};
getLocalIP(ip => console.log('Local IP:', ip));
注意事项:
- 隐私合规:收集设备信息需遵循 GDPR、CCPA 等隐私法规,明确告知用户并获取同意。
- 不可靠性:设备指纹可能因浏览器设置、系统更新或用户行为(如隐私模式)而变化。
- 结合后端:可结合 IP、登录会话或 Cookie 增强识别稳定性。
替代方案
- 持久化存储:使用
localStorage
或IndexedDB
存储生成的 UUID。 - 用户登录系统:通过账号体系追踪用户而非设备。
如果需要真正的硬件级唯一标识,通常需通过原生应用(如 Electron、React Native)或与后端配合实现。