背景
目前网站服务器和浏览器是没办法知道你是谁,因此只能通过账号注册,网页上登录,服务器给你的浏览器发一个令牌,浏览器储存令牌来标记你的身份,你后续发的请求都会携带这个令牌,让服务器记住你。
Chrome一直在阻止网页技术捕获用户的指纹 fingerprint,这是出于对于用户隐私与安全性的考虑,所以现在可用的技术可能在未来的某个版本中就不可用了。
但有时候我们确实是想记录用户浏览器的信息(设备id、电脑id),来分辨访问网站的用户,或者识别是不是多开浏览器,就只能靠其他方式。
canvas指纹
说明
canvas 是JS的一个Web API,原本是用于绘制图形的,相当于window里面的画图软件。
canvas API提供了一个通过JavaScript 和 HTML的
canvas指纹理论上可以唯一标识一个浏览器,即使用户删除了浏览器的任何隐私记录(例如cookie,localStorage,indexedDB等等),这个值在每次生成的时候都依然是相同的。
参考:https://zhuanlan.zhihu.com/p/67923680
工作原理
用JS创建一个canvas画布,然后在画布上面画几个图形,正方形,圆形等,然后写几个字。
然后把这个canvas base64编码,最后生成base64编码的hash值,这个hash值就是canvas指纹。(hash算法可以使用非常主流的md5算法)。
因为这个hash值本质上是浏览器+操作系统+GPU+图形驱动器的唯一性,任何俩个不相同的浏览器,或者不同电脑上面的相同浏览器 都会存在细丝末毫的不同,而这个不同会导致浏览器绘制的图像肉眼看起来相同,但是某几个像素点可能存在几个像素的偏移或者灰度偏移,然后生成的hash值就完全不一样了。
那么它是百分百准确的吗?
不是!在其他程序猿测试过程中发现,这个指纹在最新的Chrome浏览器上(version74)显示都是相同的了,或者说相同几率很高。很可能Google Chrome团队已经解决底层操作系统与硬件差异带来的这个问题了。
但是这项技术依然被广泛采用,因为根据这项技术衍生的技术还有 audio指纹,webgl指纹,fonts字体指纹。这些技术的组合叫做浏览器指纹。
实现
/** ================= utf16编码 转 十六进制 函数================= */
function int16_to_hex(i:number) {
let result = i.toString(16)
let j = 0
while (j + result.length < 4) {
result = "0" + result
j++
}
return result
}
/** ================= 二进制数据 转 十六进制数据 函数 ================= */
function bin2hex(str:string) {
let result = ""
for (let i = 0; i < str.length; i++) {
result += int16_to_hex(str.charCodeAt(i))
}
return result
}
/** ================= canvas指纹函数 ================= */
/** 获取 基于 canvas 生成的浏览器指纹,重复率比较低,可以认为是设备id */
export function getDeviceId() {
// 1、创建一个 canvas 画布对象
let canvas = document.createElement("canvas")
// 2、创建一个 2d 二维的画布
let ctx = canvas.getContext("2d")
// 3、设置给画布显示的内容,随便什么内容都可以
let txt = "yjl123123123 <canvas> 1.0"
// 4、给画布渲染内容
if(ctx){
// 设置基线 top 是顶部对齐
ctx.textBaseline = "top";
// 设置字体
ctx.font = "14px 'Arial'";
// 设置基线 alphabetic 是文本基线是标准的字母基线
ctx.textBaseline = "alphabetic";
// 设置颜色
ctx.fillStyle = "#f60";
// 画一个矩形,在x为125、y为1的位置,画一个宽62px、高20px的矩形
ctx.fillRect(125,1,62,20);
// 设置另一种颜色
ctx.fillStyle = "#069";
// 渲染文字,坐标是x:2,y:15
ctx.fillText(txt, 2, 15);
// 设置另一种颜色
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
// 渲染文字,坐标是x:4,y:17
ctx.fillText(txt, 4, 17);
}
// 5、转换操作,使用canvas.toDataURL()方法获得图片内容的base64编码(对于PNG格式的图片,以块(chunk)划分,最后一块是32位CRC校验)作为唯一性标识
// 通过toDataURL的方式,获取上面创建的画布的base64类型的数据,并替换掉前缀
let b64 = canvas.toDataURL().replace("data:image/png;base64,", "")
// 把base64类型数据转换成二进制
let bin = window.atob(b64)
// 取二进制的特定的一小段,转换成16进制
let crc = bin2hex(bin.slice(-16, -12))
return crc
}