下面这张图,不管是IM开拓者还是普通用户,该当很熟习:
于是,搞IM产品的老板和产品经理们,从此又多了一个要抛给程序员们的需求——“为什么微信有扫一扫登录,而我们的没有?”。
好吧,每次只假如微信有的功能,IM程序员们想甩锅,难度就有点大了,必竟老板们都都会想当然认为,微信有的“我”的IM产品里也得有。

既然无法回避,那就只能老诚笃实搞懂技能事理,然后自已使劲撸吧。
本文将简要的先容扫码登录功能的技能实现逻辑,并实际结合淘宝、微信的扫码登录功能,学习和研究大厂主流运用的技能实现思路。
学习互换:
- 即时通讯/推送技能开拓互换5群:215477170 [推举]
- 移动端IM开拓入门文章:《新手入门一篇就够:从零开拓移动端IM》
(本文同步发布于:http://www.52im.net/thread-2892-1-1.html)
2、IM开拓干货系列文章本文是系列文章中的第22篇,总目录如下:
《IM投递担保机制实现(一):担保在线实时的可靠投递》
《IM投递担保机制实现(二):担保离线的可靠投递》
《如何担保IM实时的“时序性”与“同等性”?》
《IM单聊和群聊中的在线状态同步该当用“推”还是“拉”?》
《IM群聊如此繁芜,如何担保不丢不重?》
《一种Android端IM智能心跳算法的设计与实现磋商(含样例代码)》
《移动端IM登录时拉取数据如何作到省流量?》
《普通易懂:基于集群的移动端IM接入层负载均衡方案分享》
《浅谈移动端IM的多点上岸和漫游事理》
《IM开拓根本知识补课(一):精确理解前置HTTP SSO单点上岸接口的事理》
《IM开拓根本知识补课(二):如何设计大量图片文件的做事端存储架构?》
《IM开拓根本知识补课(三):快速理解做事端数据库读写分离事理及实践建议》
《IM开拓根本知识补课(四):精确理解HTTP短连接中的Cookie、Session和Token》
《IM群聊的已读回执功能该怎么实现?》
《IM群聊究竟是存1份(即扩散读)还是存多份(即扩散写)?》
《IM开拓根本知识补课(五):普通易懂,精确理解并用好MQ行列步队》
《一个低本钱确保IM时序的方法磋商》
《IM开拓根本知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!
》
《IM里“附近的人”功能实现事理是什么?如何高效率地实现它?》
《IM开拓根本知识补课(七):主流移动端账号登录办法的事理及设计思路》
《IM开拓根本知识补课(八):史上最普通,彻底搞懂字符乱码问题的实质》
《IM的扫码登功能如何实现?一文搞懂主流运用的扫码上岸技能事理》(本文)
3、基本技能事理3.1 扫码登录功能到底是什么样的?首先先容下什么是扫码登录。现在大部分同学手机上都装有微信、qq和淘宝这一类的软件。而这些app都有他们相对应的网页端。为了让用户在利用他们的网页时登录更加方便和安全,利用手机扫一扫就可以登录的做事,就显的自然而然了。
几个主流大厂运用扫码登录时的界面效果如下:
有很多小伙伴可能会感到很神奇,网页上只是显示了个二维码,它怎么就知道是哪个手机扫到了二维码,并且进行登录的呢?而且,登录完成往后,还能直接把用户信息显示给用户,真的是很神奇啊。
3.2 扫码登录功能的完全技能逻辑1)网页端与做事器的合营逻辑:
接下来便是对付这个做事的详细实现。
首先用户打开网站的登录页面的时候,向浏览器的做事器发送获取登录二维码的要求。做事器收到要求后,随机天生一个uuid,将这个id作为key值存入redis做事器,同时设置一个过期韶光,再过期后,用户登录二维码须要进行刷新重新获取。
同时,将这个key值和本公司的验证字符串合在一起,通过二维码天生接口,天生一个二维码的图片(二维码天生,网上有很多现成的接口和源码,这里不再先容)。然后,将二维码图片和uuid一起返回给用户浏览器。
浏览器拿到二维码和uuid后,会每隔一秒向浏览器发送一次,登录是否成功的要求。要求中携带有uuid作为当前页面的标识符。这里有的同学就会奇怪了,做事器只存了个uuid在redis中作为key值,怎么会有用户的id信息呢?
这里确实会有用户的id信息,这个id信息是由手机做事器存入redis中的。详细请连续阅读“手机端与做事器的合营逻辑”。
2)手机端与做事器的合营逻辑:
话说,浏览器拿到二维码后,将二维码展示到网页上,并给用户一个提示:请取出您的手机,打开扫一扫进行登录。
用户拿脱手机扫描二维码,就可以得到一个验证信息和一个uuid(扫描二维码获取字符串的功能在网上同样有很多demo,这里就不详细先容了)。
由于手机端已经进行过了登录,在访问手机真个做事器的时候,参数中都回携带一个用户的token,手机端做事器可以从中解析到用户的userId(这里从token中取值而不是手机端直接传userid是为了安全,直接传userid可能会被截获和修正,token是加密的,被修正的风险会小很多)。手机端将解析到的数据和用户token一起作为参数,向做事器发送验证登录要求(这里的做事器是手机做事器,手机真个做事器跟网页端做事器不是同一台做事器)。
做事器收到要求后,首先比拟参数中的验证信息,确定是否为用户登录要求接口。如果是,返回一个确认信息给手机端。
手机端收到返回后,将登录确认框显示给用户(防止用户误操作,同时使登录更加人性化)。用户确认是进行的登录操作后,手机再次发送要求。做事器拿到uuId和userId后,将用户的userid作为value值存入redis中以uuid作为key的键值对中。
3)登录成功时的逻辑:
然后,浏览器再次发送要求的时候,浏览器真个做事器就可以得到一个用户Id,并调用登录的方法,声成一个浏览器真个token,再浏览器再次发送要求的时候,将用户信息返回给浏览器,登录成功。这里存储用户id而不是直接存储用户信息是由于,手机真个用户信息,不一定是和浏览器真个用户信息完备同等。
4)详细的技能事理总结如下图所示:
4、淘宝的扫码登录技能实现
本节我们以淘宝的扫码登录为例,来实际研究剖析一下淘宝的扫码登录实现逻辑。
登录界面 https://login.taobao.com/member/login.jhtml 传回来的参数为:
然后要求(GET)报文是这样的:
1https://qrlogin.taobao.com/qrcodelogin/qrcodeLoginCheck.do?lgToken=2c3b4d53ef0513787bf4ce711ea5ba53&defaulturl=&_ksTS=1540106757739_2804&callback=jsonp2805
关键的便是lgToken,是网页的唯一ID,当打开了二维码登录的时候,网页在轮询(该当是长轮询long polling)调用接口去要求做事器。
如果没有扫码,返回的为:
如果扫了的话则会返回:
{
"code": "10001",
"message": "mobile scan QRCode success",
"success": true
}
永劫光没有扫码的话,网页端会停滞轮询,二维码失落效!
当手机端确认登录后,接口返回的是:
{ "code": "10006", "success": true, "url": "https://login.taobao.com/member/loginByIm.do?uid=cntaobaoxxx&token=ff82fc0d1d395a33d3b38ec5a4981336&time=1530179143250&asker=qrcodelogin&ask_version=1.0.0&defaulturl=https://www.taobao.com&webpas=0b7aed2d43f01825183e4a49c6cae47d1479929926"}
表示登录成功,当然手机端与做事端在点击"确认登录"之间的交互可能便是这样:网页端天生的lgToken去要求做事端,做事端记住了这个lgToken并认为登录了,当网页端再次轮询要求接口时,就返回真正的登录态Token,网页端此时就可以凭着这个Token来登录了。
详细的技能逻辑如下图所示:
5、微信的扫码登录技能实现5.1 技能事理流程图
微信的网页版访问地址是:https://wx.qq.com/,有兴趣也可以自行深入研究。
5.2 实际的技能实现逻辑1)获取唯一的uuid, 以及包含uid信息的二维码:
// 获取uuid
getUUID: function() {
vare = t.defer();
returnwindow.QRLogin = {},
$.ajax({
url: i.API_jsLogin,
dataType: "script"
}).done(function() {
200 == window.QRLogin.code ? e.resolve(window.QRLogin.uuid) : e.reject(window.QRLogin.code)
}).fail(function() {
e.reject()
}),
e.promise
}
2)浏览器轮询做事器,获取扫码状态:
// 查看扫码状态
checkLogin: function(e, a) {
varn = t.defer()
, a = a || 0;
returnwindow.code = 0,
window.checkLoginPromise = $.ajax({
url: i.API_login + "?loginicon=true&uuid="+ e + "&tip="+ a + "&r="+ ~newDate,
dataType: "script",
timeout: 35e3
}).done(function() {
newRegExp("/"+ location.host + "/");
if(window.redirect_uri && window.redirect_uri.indexOf("/"+ location.host + "/") < 0)
returnvoid (location.href = window.redirect_uri);
vare = {
code: window.code,
redirect_uri: window.redirect_uri,
userAvatar: window.userAvatar
};
n.resolve(e)
}).fail(function() {
n.reject()
}),
n.promise
}
3)根据做事器返回的扫码状态,进行相应的操作:
408 扫码超时:如果手机没有扫码或没有授权登录,做事器会壅塞约25s,然后返回状态码 408 -> 前端连续轮询
400 二维码失落效:大约5分钟的韶光内不扫码,二维码失落效
201 已扫码:如果手机已经扫码,做事器立即返回状态码和用户的基本信息 (window.code=201,window.code.userAvator="..."),-> 前端连续轮询
200 已授权:如果手机点击了确认登录,做事器返回200及token -> 前端停滞轮询, 获取到token,重定向到目标页
详细的代码示例如下:
// 根据做事器返回的扫码状态,进行相应的操作
functiono(c) {
switch(c.code) {
case200:
t.newLoginPage(c.redirect_uri).then(function(t) {
varo = t.match(/<ret>(.)<\/ret>/)
, r = t.match(/<script>(.)<\/script>/)
, c = t.match(/<skey>(.)<\/skey>/)
, s = t.match(/<wxsid>(.)<\/wxsid>/)
, l = t.match(/<wxuin>(.)<\/wxuin>/)
, d = t.match(/<pass_ticket>(.)<\/pass_ticket>/)
, f = t.match(/<message>(.)<\/message>/)
, u = t.match(/<redirecturl>(.)<\/redirecturl>/);
returnu ? void (window.location.href = u[1]) : o && "0"!= o[1] ? (alert(f && f[1] || "登录失落败"),
i.report(i.AUTH_FAIL_COUNT, 1),
void location.reload()) : (e.$emit("newLoginPage", {
Ret: o && o[1],
SKey: c && c[1],
Sid: s && s[1],
Uin: l && l[1],
Passticket: d && d[1],
Code: r
}),
void (a.getCookie("webwx_data_ticket") || n.report(n.ReportType.cookieError, {
text: "webwx_data_ticket 票据丢失",
cookie: document.cookie
})))
});
break;
case201:
e.isScan = !0,
n.report(n.ReportType.timing, {
timing: {
scan: Date.now()
}
}),
t.checkLogin(e.uuid).then(o, function(t) {
!t && window.checkLoginPromise && (e.isBrokenNetwork = !0)
});
break;
case408:
t.checkLogin(e.uuid).then(o, function(t) {
!t && window.checkLoginPromise && (e.isBrokenNetwork = !0)
});
break;
case400:
case500:
case0:
vars = a.getCookie("refreshTimes") || 0;
s < 5 ? (s++,
a.setCookie("refreshTimes", s, .5),
document.location.reload()) : e.isNeedRefresh = !0;
break;
case202:
e.isScan = !1,
e.isAssociationLogin = !1,
a.setCookie("login_frequency", 0, 2),
window.checkLoginPromise && (window.checkLoginPromise.abort(),
window.checkLoginPromise = null),
r()
}
e.code = c.code,
e.userAvatar = c.userAvatar,
a.log("get code", c.code)
}
5.3 小结微信网页端扫码登录时,轮询的数据返回采取的是JSONP的形式,这是为理解决跨域问题。如对JSONP不理解的,可以阅读《详解Web端通信办法的演进:从Ajax、JSONP 到 SSE、Websocket》一文的“三、JSONP”这一节。
微信网页端扫码登录时,轮询采取了后台根据扫码情形壅塞前台要求,优化轮询及减少前真个无效轮询。这种技能,请详见《新手入门贴:史上最全Web端即时通讯技能事理详解》一文中的“办理方案3.2:长轮询(long-polling)”这一节,
6、本文小结扫码登录这个功能,现在已经不但涌现有IM运用里,各种带有移动真个线上网站也都有了这个功能,以是本文中先容的技能事理并不局限于只用于实现IM运用中的扫码登录。
其余,为了方便抓取真实的数据进行剖析研究,本文中的PC端案例剖析是针对的是网页端,但实际上如果你的PC端是富客户端(也便是.exe、.dmg这样的安装版),事理也是一样的,而且还不须要考虑浏览器里的跨域问题等。
阅读本文时,可能涉及到传统的Web端即时通讯技能(为了扫码登录的实时性),比如长轮询等,如果您对这些技能还不太理解的话,可以系统学习一下即时通讯网整理的有关Web端即时通讯方面的资料。比如从这两篇文章开始:《新手入门贴:详解Web端即时通讯技能的事理》、《Web端即时通讯技能盘点:短轮询、Comet、Websocket、SSE》。
附录:更多IM开拓干系文章[1] IM开拓热门文章:
《新手入门一篇就够:从零开拓移动端IM》
《移动端IM开拓者必读(一):普通易懂,理解移动网络的“弱”和“慢”》
《移动端IM开拓者必读(二):史上最全移动弱网络优化方法总结》
《从客户真个角度来谈谈移动端IM的可靠性和投递机制》
《当代移动端网络短连接的优化手段总结:要求速率、弱网适应、安全保障》
《腾讯技能分享:社交网络图片的带宽压缩技能演进之路》
《小白必读:闲话HTTP短连接中的Session和Token》
《移动端IM开拓须要面对的技能问题》
《开拓IM是自己设计协议用字节流好还是字符流好?》
《叨教有人知道语音留言谈天的主流实现办法吗?》
《普通易懂:基于集群的移动端IM接入层负载均衡方案分享》
《微信对网络影响的技能试验及剖析(论文全文)》
《即时通讯系统的事理、技能和运用(技能论文)》
《开源IM工程“蘑菇街TeamTalk”的现状:一场有始无终的开源秀》
《QQ音乐团队分享:Android中的图片压缩技能详解(上篇)》
《QQ音乐团队分享:Android中的图片压缩技能详解(下篇)》
《腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速率和成功率》
《腾讯原创分享(二):如何大幅压缩移动网络下APP的流量花费(上篇)》
《腾讯原创分享(三):如何大幅压缩移动网络下APP的流量花费(下篇)》
《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》
《基于社交网络的Yelp是如何实现海量用户图片的无损压缩的?》
《腾讯技能分享:腾讯是如何大幅降落带宽和网络流量的(图片压缩篇)》
《腾讯技能分享:腾讯是如何大幅降落带宽和网络流量的(音视频技能篇)》
《字符编码那点事:快速理解ASCII、Unicode、GBK和UTF-8》
《全面节制移动端主流图片格式的特点、性能、调头等》
《子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技能实践》
《微信技能分享:微信的海量IM谈天序列号天生实践(算法事理篇)》
《自已开拓IM有那么难吗?手把手教你自撸一个Andriod版大略单纯IM (有源码)》
《融云技能分享:解密融云IM产品的谈天ID天生策略》
《适宜新手:从零开拓一个IM做事端(基于Netty,有完全源码)》
《拿起键盘便是干:跟我一起徒手开拓一套分布式IM系统》
>> 更多同类文章 ……
[2] 有关WEB端即时通讯开拓:
《新手入门贴:史上最全Web端即时通讯技能事理详解》
《Web端即时通讯技能盘点:短轮询、Comet、Websocket、SSE》
《SSE技能详解:一种全新的HTML5做事器推送事宜技能》
《Comet技能详解:基于HTTP长连接的Web端实时通信技能》
《新手快速入门:WebSocket简明教程》
《WebSocket详解(一):初步认识WebSocket技能》
《WebSocket详解(二):技能事理、代码演示和运用案例》
《WebSocket详解(三):深入WebSocket通信协议细节》
《WebSocket详解(四):刨根问底HTTP与WebSocket的关系(上篇)》
《WebSocket详解(五):刨根问底HTTP与WebSocket的关系(下篇)》
《WebSocket详解(六):刨根问底WebSocket与Socket的关系》
《socket.io实现推送的一点实践及思路》
《LinkedIn的Web端即时通讯实践:实现单机几十万条长连接》
《Web端即时通讯技能的发展与WebSocket、Socket.io的技能实践》
《Web端即时通讯安全:跨站点WebSocket挟制漏洞详解(含示例代码)》
《开源框架Pomelo实践:搭建Web端高性能分布式IM谈天做事器》
《利用WebSocket和SSE技能实现Web端推送》
《详解Web端通信办法的演进:从Ajax、JSONP 到 SSE、Websocket》
《MobileIMSDK-Web的网络层框架为何利用的是Socket.io而不是Netty?》
《理论联系实际:从零理解WebSocket的通信事理、协议格式、安全性》
《微信小程序中如何利用WebSocket实现长连接(含完全源码)》
《八问WebSocket协议:为你快速解答WebSocket热门疑问》
《快速理解Electron:新一代基于Web的跨平台桌面技能》
《一文读懂前端技能演进:盘点Web前端20年的技能变迁史》
《Web端即时通讯根本知识补课:一文搞懂跨域的所有问题!
》
>> 更多同类文章 ……
(本文同步发布于:http://www.52im.net/thread-2892-1-1.html)