背景

目前网站服务器和浏览器是没办法知道你是谁,因此只能通过账号注册,网页上登录,服务器给你的浏览器发一个令牌,浏览器储存令牌来标记你的身份,你后续发的请求都会携带这个令牌,让服务器记住你。

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字体指纹。这些技术的组合叫做浏览器指纹。

实现

  1. /** ================= utf16编码 转 十六进制 函数================= */
  2. function int16_to_hex(i:number) {
  3. let result = i.toString(16)
  4. let j = 0
  5. while (j + result.length < 4) {
  6. result = "0" + result
  7. j++
  8. }
  9. return result
  10. }
  11. /** ================= 二进制数据 转 十六进制数据 函数 ================= */
  12. function bin2hex(str:string) {
  13. let result = ""
  14. for (let i = 0; i < str.length; i++) {
  15. result += int16_to_hex(str.charCodeAt(i))
  16. }
  17. return result
  18. }
  19. /** ================= canvas指纹函数 ================= */
  20. /** 获取 基于 canvas 生成的浏览器指纹,重复率比较低,可以认为是设备id */
  21. export function getDeviceId() {
  22. // 1、创建一个 canvas 画布对象
  23. let canvas = document.createElement("canvas")
  24. // 2、创建一个 2d 二维的画布
  25. let ctx = canvas.getContext("2d")
  26. // 3、设置给画布显示的内容,随便什么内容都可以
  27. let txt = "yjl123123123 <canvas> 1.0"
  28. // 4、给画布渲染内容
  29. if(ctx){
  30. // 设置基线 top 是顶部对齐
  31. ctx.textBaseline = "top";
  32. // 设置字体
  33. ctx.font = "14px 'Arial'";
  34. // 设置基线 alphabetic 是文本基线是标准的字母基线
  35. ctx.textBaseline = "alphabetic";
  36. // 设置颜色
  37. ctx.fillStyle = "#f60";
  38. // 画一个矩形,在x为125、y为1的位置,画一个宽62px、高20px的矩形
  39. ctx.fillRect(125,1,62,20);
  40. // 设置另一种颜色
  41. ctx.fillStyle = "#069";
  42. // 渲染文字,坐标是x:2,y:15
  43. ctx.fillText(txt, 2, 15);
  44. // 设置另一种颜色
  45. ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
  46. // 渲染文字,坐标是x:4,y:17
  47. ctx.fillText(txt, 4, 17);
  48. }
  49. // 5、转换操作,使用canvas.toDataURL()方法获得图片内容的base64编码(对于PNG格式的图片,以块(chunk)划分,最后一块是32位CRC校验)作为唯一性标识
  50. // 通过toDataURL的方式,获取上面创建的画布的base64类型的数据,并替换掉前缀
  51. let b64 = canvas.toDataURL().replace("data:image/png;base64,", "")
  52. // 把base64类型数据转换成二进制
  53. let bin = window.atob(b64)
  54. // 取二进制的特定的一小段,转换成16进制
  55. let crc = bin2hex(bin.slice(-16, -12))
  56. return crc
  57. }