阿里面试官:分别说说微信和淘宝扫码登录背后的实现原理?
作者:imtech
my.oschina.net/u/4231722/blog/3154805
1、引言
于是,搞IM产品的老板和产品经理们,从此又多了一个要抛给程序员们的需求——“为什么微信有扫一扫登录,而我们的没有?”。
好吧,每次只要是微信有的功能,IM程序员们想甩锅,难度就有点大了,毕竟老板们都都会想当然认为,微信有的“我”的IM产品里也得有。
2、基本技术原理
2.1 扫码登录功能到底是什么样的?
有很多小伙伴可能会感到很神奇,网页上只是显示了个二维码,它怎么就知道是哪个手机扫到了二维码,并且进行登录的呢?而且,登录完成以后,还能直接把用户信息显示给用户,真的是很神奇啊。
2.2 扫码登录功能的完整技术逻辑
1)网页端与服务器的配合逻辑:
4)详细的技术原理总结如下图所示:
3、淘宝的扫码登录技术实现
本节我们以淘宝的扫码登录为例,来实际研究分析一下淘宝的扫码登录实现逻辑。
登录界面 https://login.taobao.com/member/login.jhtml 传回来的参数为:
然后请求(GET)报文是这样的:
https://qrlogin.taobao.com/qrcodelogin/qrcodeLoginCheck.do?
lgToken=2c3b4d53ef0513787bf4ce711ea5ba53&defaulturl=&_ksTS=1540106757739_2804&callback=jsonp2805
关键的就是lgToken,是网页的唯一ID,当打开了二维码登录的时候,网页在轮询(应该是长轮询long polling)调用接口去请求服务器。扩展:彻底理解cookie,session,token
如果没有扫码,返回的为:
如果扫了的话则会返回:
{
"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"
}
4、微信的扫码登录技术实现
4.1 技术原理流程图
4.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)
}
4.3 小结
微信网页端扫码登录时,轮询的数据返回采用的是JSONP的形式,这是为了解决跨域问题。如对JSONP不了解的,可以参考:
http://www.52im.net/thread-1038-1-1.html
微信网页端扫码登录时,轮询采用了后台根据扫码情况阻塞前台请求,优化轮询及减少前端的无效轮询。这种技术,请详见:
http://www.52im.net/thread-338-1-1.html
5、本文小结
END
1、GitHub 标星 3.2w!史上最全技术人员面试手册!FackBoo发起和总结
5、37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...