185 lines
5.8 KiB
JavaScript
185 lines
5.8 KiB
JavaScript
// 使用IIFE封装防止污染全局变量
|
|
(function (window) {
|
|
// 配置参数
|
|
const defaultConfig = {
|
|
webgl: true,
|
|
audio: true,
|
|
deviceInfo: true,
|
|
};
|
|
|
|
// 创建全局对象
|
|
window.DeviceFingerprint = {
|
|
init: function (config = {}) {
|
|
// 等待DOM加载完成
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', () => this._initialize(config));
|
|
} else {
|
|
this._initialize(config);
|
|
}
|
|
return this;
|
|
},
|
|
// 新增实际初始化方法
|
|
_initialize: function (config) {
|
|
this.config = { ...defaultConfig, ...config };
|
|
this._loadDependencies()
|
|
.then(() => {
|
|
this._injectCanvasElements();
|
|
})
|
|
.catch((error) => {
|
|
console.error('依赖加载失败:', error);
|
|
});
|
|
},
|
|
// 自动注入需要的canvas元素
|
|
_injectCanvasElements: function () {
|
|
if (!document.getElementById('fp-canvas')) {
|
|
const canvas = document.createElement('canvas');
|
|
canvas.id = 'fp-canvas';
|
|
canvas.style.display = 'block';
|
|
document.body.appendChild(canvas);
|
|
}
|
|
},
|
|
_getIP: async function () {
|
|
const endpoints = [
|
|
// 'https://api.ipify.org?format=json', // 主接口
|
|
'https://ipapi.co/json/', // 备用接口1
|
|
// 'https://ipinfo.io/json' // 备用接口2
|
|
];
|
|
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
|
|
try {
|
|
// 尝试所有接口直到成功
|
|
for (const url of endpoints) {
|
|
try {
|
|
const response = await fetch(url, {
|
|
signal: controller.signal,
|
|
});
|
|
|
|
if (!response.ok) throw new Error('响应异常');
|
|
|
|
const data = await response.json();
|
|
return data.ip || data.ipAddress || '未知IP';
|
|
} catch (error) {
|
|
console.warn(`接口 ${url} 请求失败:`, error.message);
|
|
}
|
|
}
|
|
return '获取失败';
|
|
} catch (error) {
|
|
console.error('IP获取最终失败:', error);
|
|
return '获取失败';
|
|
} finally {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
},
|
|
// 统一获取所有指纹
|
|
async getAllFingerprints() {
|
|
// 等待MD5库加载
|
|
if (!window.hex_md5) {
|
|
await this._loadDependencies();
|
|
}
|
|
|
|
const results = {};
|
|
// 添加null检查
|
|
if (!document.getElementById('fp-canvas')) {
|
|
throw new Error('Canvas元素未成功创建');
|
|
}
|
|
if (this.config.webgl) {
|
|
results.webgl = this._getWebGLFingerprint();
|
|
}
|
|
if (this.config.deviceInfo) {
|
|
results.device = this._getDeviceFingerprint();
|
|
}
|
|
return results;
|
|
},
|
|
getWebGLFingerprint: async function () {
|
|
// 等待依赖加载完成
|
|
if (!window.hex_md5) {
|
|
await this._loadDependencies();
|
|
}
|
|
try {
|
|
// 检查canvas元素是否存在
|
|
if (!document.getElementById('fp-canvas')) {
|
|
throw new Error('Canvas元素未找到,请先调用初始化方法');
|
|
}
|
|
// 调用内部方法获取指纹
|
|
return this._getWebGLFingerprint();
|
|
} catch (error) {
|
|
console.error('获取WebGL指纹失败:', error);
|
|
return null;
|
|
}
|
|
},
|
|
_getWebGLFingerprint: async function () {
|
|
const canvas = document.createElement('canvas');
|
|
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
|
|
if (!gl) return 'unsupported';
|
|
// 获取WebGL基础参数
|
|
const params = [
|
|
gl.getParameter(gl.VERSION),
|
|
gl.getParameter(gl.SHADING_LANGUAGE_VERSION),
|
|
gl.getParameter(gl.VENDOR),
|
|
gl.getParameter(gl.RENDERER),
|
|
gl.getParameter(gl.MAX_TEXTURE_SIZE),
|
|
gl.getParameter(gl.MAX_RENDERBUFFER_SIZE),
|
|
];
|
|
|
|
// 获取屏幕尺寸
|
|
const screen = `${window.screen.width}x${window.screen.height}x${window.screen.colorDepth}`;
|
|
console.log('screen', screen);
|
|
console.log('params', params);
|
|
// 组合关键特征
|
|
const fingerprintData = `${params.join('|')}|${screen}`;
|
|
return hex_md5(fingerprintData);
|
|
},
|
|
_getDeviceFingerprint: function () {
|
|
try {
|
|
const deviceInfo = {
|
|
screen: `${screen.width}x${screen.height}`,
|
|
colorDepth: screen.colorDepth,
|
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
platform: navigator.platform,
|
|
userAgent: navigator.userAgent,
|
|
languages: navigator.languages.join(','),
|
|
hardwareConcurrency: navigator.hardwareConcurrency || 'unknown',
|
|
};
|
|
|
|
return hex_md5(JSON.stringify(deviceInfo));
|
|
} catch (error) {
|
|
console.error('Device info error:', error);
|
|
return null;
|
|
}
|
|
},
|
|
// 修改MD5加载方法
|
|
_loadDependencies: function () {
|
|
return new Promise((resolve, reject) => {
|
|
if (window.hex_md5) return resolve();
|
|
|
|
const script = document.createElement('script');
|
|
script.src = './md5.js';
|
|
script.onload = resolve;
|
|
script.onerror = reject;
|
|
document.head.appendChild(script);
|
|
});
|
|
},
|
|
getWebGLFingerprint: async function () {
|
|
// 等待依赖加载完成
|
|
if (!window.hex_md5) {
|
|
await this._loadDependencies();
|
|
}
|
|
try {
|
|
// 检查canvas元素是否存在
|
|
if (!document.getElementById('fp-canvas')) {
|
|
throw new Error('Canvas元素未找到,请先调用初始化方法');
|
|
}
|
|
// 调用内部方法获取指纹
|
|
return this._getWebGLFingerprint();
|
|
} catch (error) {
|
|
console.error('获取WebGL指纹失败:', error);
|
|
return null;
|
|
}
|
|
},
|
|
};
|
|
// 自动初始化(可选)
|
|
window.DeviceFingerprint.init();
|
|
})(window);
|