总结一下使用canvas fingerprinting的过程
首先来介绍一下 canvas 指纹的信息
在过去,实现上述cookie是最受欢迎的一种。但由于移动互联网的发展,移动设备限制、用户禁用cookie。使得cookie愈来愈不受待见。伴随着html5的成熟,通过canvas fingerprinting技术标识一个唯一的浏览器逐渐被接受。它的特点是不通过cookie,用户基本无法屏蔽它 。
需求分析
一个简单的需求看这里》》
- 组件名称:初始以及为空时,默认为【点赞n】,n按照点赞组件的添加顺序自增;
- 点赞逻辑:
(1)点赞后,再次点击可以取消点赞
(2)每个用户可以点击一次 - 页面展示效果:点赞/取消点赞时,显示心跳的动画效果。
- 点赞次数
- 是否登陆
- 登陆了
- 记录userId判断是否点赞
- 没登录
- 1:会出现的问题:
- 什么时机产生
-
方案一
- 用户打开页面,获取cookie/seesion,生成一个唯一的识别码
- uuid,储存下来,前端再进行二次判断是否此用户出现过。
-
方案二
- 使用canvas fingerprinter(由前端根据用户浏览器的各种信息【比如分辨率,浏览器设定,浏览器版本,硬件资讯等】生成一个canvas)比如获取浏览器的序列号;
-
怎么获取浏览器信息:
结论--canvas fingerprinting帆布指纹技术
使用方案二
-
- 产生以后怎么保证 同一个人映射唯一的 uuid
- 只保证同一个浏览器看做一个用户
- 几种场景
- 安卓
- 微信
- 各种浏览器(chrome,safari,firefox,UC,qq,猎豹,opera,自带的。。)
- ios
- 微信
- 各种浏览器
- pc
- 各种浏览器
- ie9-
- 平板
- ipad
- 其他平板
- 可能会出现的问题
- 一个人多刷
- 无痕模式
- 用户手动清除cookie
- 安卓
- 什么时机产生
- 2 #流程
- 登陆
- 根据userId判断是否点赞
- 未登录
- 用户打开浏览器
- 根据浏览器的各种信息,前端生成一个canvas fingerprinting 图(一串b64 string代码)
- 返回给后台,记录下来
- 根据canvas指纹的不同确认未登录的用户
- 登陆
- 3 考虑的问题
- 返回的bs64太长,要简单点 截一段
- 改动canvas使其简单短一点
- 数据类型统一为 string
- 1:会出现的问题:
- 登陆了
》和后台讨论完问题后开始查找资料和撸代码
其实除了帆船指纹方法还有很多其他的前端的方法用以追踪用户信息,我也顺便研究了一下❤️,后面再留链接大家看看;
方案一
<script>
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
ctx.font = "24px Arial";
ctx.fillText("Hello Panda",22,33);
ctx.moveTo(0,60);
ctx.lineTo(100,60);
ctx.stroke();
//大家就随意创建一个canvas标签就是
var b64 = canvas.toDataURL().replace("data:image/png;base64,","");
//然后用toDataURL方法对生成的canvas图像进行64码进制转换
console.log(b64);
//然后我们打印出来看看效果
</script>
//一开始使用这个产生的代码
- 效果如下不堪入目,不过几乎没有重复率的说
- 下面是效果图我测试了几个浏览器的不同情况
- chrome
- 正常模式
- [图片上传失败...(image-baf153-1516330888974)]
- 正常模式
- chrome
- 隐身模式
- [图片上传失败...(image-bcfd33-1516330888974)]
- firefox
- 正常模式
- [图片上传失败...(image-a1e262-1516330888974)]
- 隐身模式
- [图片上传失败...(image-8ef4d0-1516330888974)](假装有图片)
- safari
- 正常模式
- [图片上传失败...(image-c68e76-1516330888974)](假装有图片)
- 隐身模式
- [图片上传失败...(image-67e898-1516330888974)](假装有图片)
- 经过精心的对比,发现隐身模式和正常模式下的64码是一致的
- 不信的哥们这里有图😌
- 还有考虑用户清楚cookie的情况我也做了测试,发现清楚cookie前后的
- 64位代码是一致的
- 当然还有其他的各种情况大家自己可以测试一下了;
效果测试还行但是后端的哥们就不同意了,你这字符串也太长了,数据库不好存呀
这简单咱们就截取一段代码就是了
下面是方案二
function bin2hex(s) {
// discuss at: http://phpjs.org/functions/bin2hex/
// original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// bugfixed by: Onno Marsman
// bugfixed by: Linuxworld
// improved by: ntoniazzi (http://phpjs.org/functions/bin2hex:361#comment_177616)
// example 1: bin2hex('Kev');
// returns 1: '4b6576'
// example 2: bin2hex(String.fromCharCode(0x00));
// returns 2: '00'
var i, l, o = '',
n;
s += '';
for (i = 0, l = s.length; i < l; i++) {
n = s.charCodeAt(i)
.toString(16);
o += n.length < 2 ? '0' + n : n;
}
return o;
}
function getUUID(domain) {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext("2d");
ctx.font = "24px Arial";
ctx.fillText("Hello Panda",22,33);
ctx.moveTo(0,60);
ctx.lineTo(100,60);
ctx.stroke();
//大家就随意创建一个canvas标签就是
var b64 = canvas.toDataURL().replace("data:image/png;base64,","");
//然后用toDataURL方法对生成的canvas图像进行64码进制转换
//console.log("b64="+b64);
var bin = atob(b64);
//console.log("bin="+bin);bin这里是一张图片了,解码图片
//这里使用js内置的 atob()方法将64进制的解码一下
var crc = bin2hex(bin.slice(-16, -12));
//console.log("crc="+crc)
return crc;
}
//使用atob函数解码
//getUUID(string) 写入一个字符串随机产生一个八位的字符串标识符
console.log(getUUID("asdfa"))
- 然后现在就生成了一个8位的字符串,好用多了。不过要经过严密的测试,还怕有重复的,这就要麻烦下后台的哥们了。测试大家下来也可以自己多搞一下。
- [图片上传失败...(image-9f2a6b-1516330888974)]
最后啦,当然是为了填充getUUID方法获取用户的一些关键性信息了;
这里贴一些获取信息的方法,其实还有很多其他的方法可以获取用户信息,我们把这些信息打包成一个长字符串,返回到getUUID方法,再打包返回给后台就大功告成了😌;
- 测试图片:
<!DOCTYPE html>
<head>
<title> New Document </title>
<meta name="viewport" content="width=device-width, initial-scale=1.0,user-scalable=no">
<meta name="Generator" content="EditPlus">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta charset="utf-8" />
<meta name="Description" content="">
<script id=clientEventHandlersJS language=javascript>
/******************************************下为浏览器信息************************************************/
//获取浏览器相关信息
function allinfo() {
var appName = navigator.appName; //浏览器的正式名称
var appVersion = navigator.appVersion; //浏览器的版本号
var cookieEnabled = navigator.cookieEnabled; // 返回用户浏览器是否启用了cookie
var cpuClass = navigator.cpuClass; //返回用户计算机的cpu的型号,通常intel芯片返回"x86"(火狐没有)
var mimeType = navigator.mimeTypes; // 浏览器支持的所有MIME类型的数组
var platform = navigator.platform; // 浏览器正在运行的操作系统平台,包括Win16(windows3.x)
// Win32(windows98,Me,NT,2000,xp),Mac68K(Macintosh 680x0)
// 和MacPPC(Macintosh PowerPC)
var plugins = navigator.plugins; // 安装在浏览器上的所有插件的数组
var userLanguage = navigator.userLanguage; // 用户在自己的操作系统上设置的语言(火狐没有)
var userAgent = navigator.userAgent; //包含以下属性中所有或一部分的字符串:appCodeName,appName,appVersion,language,platform
var systemLanguage = navigator.systemLanguage; // 用户操作系统支持的默认语言(火狐没有)
var info = "<table border=1>";
</script>
</head>
<body>
</body>
</html>