文章目录

  • 扫码登陆原理
    • 浏览器打开登陆页面
      • (1)请求二维码
      • (2)通过轮询建立『长连接』
      • (3)其他操作
    • 手机扫描二维码
    • 手机确认登陆
    • 登陆流程图
    • 疑惑
    • 参考

扫码登陆原理

以微信网页版为例,看一下二维码登陆的原理。

浏览器打开登陆页面

打开页面之后,前端脚本会完成下面几个过程:

(1)请求二维码

浏览器打开页面之后,会首先向服务器发送一个请求,获得二维码,

[外链图片转存中…(img-o9zYaSOy-1577966200637)]

利用解析二维码工具可以得到二维码的内容https://login.weixin.qq.com/l/Ie00Yc04-A==,可以看出来,实际上这个二维码包含的信息实际上就是这个请求的URL

这个URL后面对应的Ie00Yc04-A==是一个全局唯一ID,它的用处就是用来识别请求登陆的客户端,如何识别后面会讲解

(2)通过轮询建立『长连接』

打开这个页面之后,浏览器会通过堵塞等待的轮询变相的建立了一个长连接,这个轮询是每间隔25秒向服务器发送一个请求<srcipt>的GET请求:

[外链图片转存中…(img-0hTjiow9-1577966200639)]

如果25秒之内用户没有扫描这个二维码,这个请求会返回200,结束它的使命。浏览器会再次发送一个请求。这个请求的地址是https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=Ie00Yc04-A==&tip=0&r=-1708735754&_=1577961552943

可以看到,通过这个请求里包含了一个uuid字段,值就是前面提到的全局唯一ID,我们可以认为这个ID就是这个所谓的长连接的ID

查看了一下微信网页版的前端代码:

function checkLoginHandler(data) {/*** code:*      200: 成功*      201:扫描成功,但未点确认*      408:未扫描*      400:未知*      500: login poll srv exception**/switch (data.code) {case 200:loginFactory.newLoginPage(data.redirect_uri).then(function(msg) {var ret = msg.match(/<ret>(.*)<\/ret>/),code = msg.match(/<script>(.*)<\/script>/),skey = msg.match(/<skey>(.*)<\/skey>/),wxsid = msg.match(/<wxsid>(.*)<\/wxsid>/),wxuin = msg.match(/<wxuin>(.*)<\/wxuin>/),passticket = msg.match(/<pass_ticket>(.*)<\/pass_ticket>/),message = msg.match(/<message>(.*)<\/message>/),redirecturl = msg.match(/<redirecturl>(.*)<\/redirecturl>/);if (redirecturl) {window.location.href = redirecturl[1];return;}if (ret && (ret[1] != '0')) {alert((message && message[1]) || '登陆失败');monitorService.report(monitorService.AUTH_FAIL_COUNT, 1);location.reload();return;}$scope.$emit('newLoginPage', {Ret: ret && ret[1],SKey: skey && skey[1],Sid: wxsid && wxsid[1],Uin: wxuin && wxuin[1],Passticket: passticket && passticket[1],Code: code});if (!utilFactory.getCookie('webwx_data_ticket')) {reportService.report(reportService.ReportType.cookieError, {text: 'webwx_data_ticket 票据丢失',cookie: document.cookie});}});break;case 201:$scope.isScan = true;reportService.report(reportService.ReportType.timing, {timing: {scan: Date.now()}});loginFactory.checkLogin($scope.uuid).then(checkLoginHandler, function(data) {if (!data && window.checkLoginPromise) {$scope.isBrokenNetwork = true;}});break;case 408:loginFactory.checkLogin($scope.uuid).then(checkLoginHandler, function(data) {if (!data && window.checkLoginPromise) {$scope.isBrokenNetwork = true;}});break;case 400:case 500:case 0:// 这里要累计次数var refreshTimes = utilFactory.getCookie('refreshTimes') || 0;if (refreshTimes < 5) {refreshTimes++;utilFactory.setCookie('refreshTimes', refreshTimes, 0.5);document.location.reload();} else {$scope.isNeedRefresh = true;}break;case 202: // 点击取消// 1. 关联登录,等待确认,取消// 2. 扫码之后,等待确认,取消$scope.isScan = false;$scope.isAssociationLogin = false;utilFactory.setCookie('login_frequency', 0, 2);// 终止轮询if (window.checkLoginPromise) {window.checkLoginPromise.abort();window.checkLoginPromise = null;}doQrcodeLogin();break;default://todo}$scope.code = data.code;$scope.userAvatar = data.userAvatar;utilFactory.log('get code', data.code);
}// 检查是否登录
checkLogin: function(uuid, tip) {var deferred = $q.defer(),tip = tip || 0;window.code = 0;// ie8window.checkLoginPromise = $.ajax({url: confFactory.API_login + '?loginicon=true&uuid=' + uuid + '&tip=' + tip + '&r=' + ~new Date(),dataType: "script",timeout: 35000}).done(function() {var reg = new RegExp('\/' + location.host + '\/')if (window.redirect_uri && window.redirect_uri.indexOf('/' + location.host + '/') < 0) {location.href = window.redirect_uri;return;}var data = {code: window.code,redirect_uri: window.redirect_uri,userAvatar: window.userAvatar};deferred.resolve(data);}).fail(function() {deferred.reject();console.log('checkLogin fail.....');});return deferred.promise;
},

关键的代码是上面两个函数,

这样当超过25s后,服务端对请求脚本的代码返回了200,但是返回的脚本的内容是什么呢?

{code: 408redirect_uri: undefineduserAvatar: undefined
}

其中的code408,然后递归调用checkLogin,可以看出来,25秒的间隔是由服务端确定的

这样在当用户登录后,微信服务器就可以将下一步的动作作为脚本返回给前端?那为什么不直接写在前端脚本中呢?

如果用户长时间没有操作,页面会自动刷新,重新执行上面的过程,保证了二维码不会因为太久没有扫描而过期

(3)其他操作

除此之外,在打开这个页面后,浏览器还会请求很多其他的资源,比如登陆的默认头像、雪碧图等等,这样当用户通过长连接后进行登陆时就不必再去请求这些资源,可以获得立刻反馈的用户体验

手机扫描二维码

当手机微信扫描这个二维码时,相当于用微信客户端,携带用户的用户名等信息,去访问了二维码对应的连接``https://login.weixin.qq.com/l/Ie00Yc04-A==`,这时候,微信登陆服务器获得了两个信息:

  1. 唯一IDIe00Yc04-A==,通过这个ID就可以找到上一步建立的长连接信息,也就找到了要登录的设备
  2. 用户的微信信息,通过这些信息也就找到了要登录的是哪个微信用户

扫描二维码后,客户端就可以通过长连接立刻获得返回的信息:

这时候,长连接返回的信息是:

{code: 201redirect_uri: undefineduserAvatar: "data:img/jpg;base64,/9j/4AAQS..." // 一个 Base64 格式的头像图片,我进行了截取
}

根据上面的代码返回了201,这个时候客户端会进行一些信息的上班,然后继续轮询,等待确认登陆

手机确认登陆

用户在客户端点击确认后,相当于向服务器发送了确认登陆的请求,服务器立刻通过轮询长连接返回信息:

{"code": 200,"redirect_uri": "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=dsfdfsdf@qrticket_0&uuid=IePV21233ddajSA==&lang=zh_CN&scan=15772296510224","userAvatar": "data:img/jpg;base64,/9j/4AAQSkZJRgA..."
}

这时,浏览器根据code200执行响应的代码,通过执行loginFactory.newLoginPage来访问上面返回的redirect_uri,根据不同的信息进行登陆或者禁止登陆的操作

以我这次登陆请求为例,不知道什么禁止我使用微信网页版,所以返回来下面的信息:

<error><ret>1203</ret><message>为了你的帐号安全,此微信号不能登录网页微信。你可以使用Windows微信或Mac微信在电脑端登录。Windows微信下载地址:https://pc.weixin.qq.com  Mac微信下载地址:https://mac.weixin.qq.com</message></error>

[外链图片转存中…(img-TXpNVBhO-1577966200642)]

如果是正常的话,就会跳转到对应的页面,开始使用微信网页版,扫码登陆的过程就到此结束。

登陆流程图

以前在头条面试的时候被问过扫码登陆的原理,并没有答的很好,面试结束后大概在网上查了查,学习的简书的这篇文章,它总结的基本流程是没有错,如下图:

[外链图片转存中…(img-VmUGgH2c-1577966200643)]

但是一些具体的技术细节并没有详细介绍,我发现自己并没有搞清楚,于是花了一会时间,又尝试的去学习了一下,还是亲自动手能够搞得更清楚。

疑惑

我还是有一个疑惑:为什么长连接的请求类型是script呢?

在jQuery的文档里查到的,当dataType设为script时的作用是:

把响应的结果当作JavaScript执行。并将其当作纯文本返回。默认情况下不会通过在URL中附加查询字符串变量_=[TIMESTAMP]进行自动缓存结果,除非设置了cache参数为true。Note: 在远程请求时(不在同一个域下),所有POST请求都将转为GET请求。(因为将使用DOM的script标签来加载)

但是好像在这里没有用到上面的任何一点,只是根据返回的对象的各个属性进行了下一步操作。那直接发送的dataTypejson格式的请求效果不是一样吗?不解。

参考

  • 微信扫描二维码登录网页是什么原理,前后两个事件是如何联系的? - 黄良懿的回答 - 知乎
  • 扫码登录是如何实现的?@简书
  • jQuery文档@jQuery123

网络基础12 二维码扫码登录原理相关推荐

  1. TCPIP远程网络电子健康码扫码设备|二维码扫码门禁机HX-QR86L-IP在校园复学防疫领域的应用

    TCPIP远程网络电子健康码扫码设备|二维码扫码门禁机HX-QR86L-IP是一款铝合金材质.带液晶显示屏,一机两用,即可做门禁读头使用,也可以做电子健康码数据采集使用.可支持静态.手机动态二维码识别 ...

  2. 面试官:“聊聊二维码扫码登录的原理”。

    开发者(KaiFaX) 面向全栈工程师的开发者 专注于前端.Java/Python/Go/PHP的技术社区 原文:https://juejin.cn/post/6940976355097985032 ...

  3. python 全栈开发,Day128(创建二维码,扫码,创建玩具的基本属性)

    python 全栈开发,Day128(创建二维码,扫码,创建玩具的基本属性) 昨日内容回顾 1.app播放音乐plus.audio.createPlayer(文件路径/URL)player.play( ...

  4. 二维码扫码登录是什么原理?

    在日常生活中,二维码出现在很多场景,比如超市支付.系统登录.应用下载等等.了解二维码的原理,可以为技术人员在技术选型时提供新的思路.对于非技术人员呢,除了解惑,还可以引导他更好地辨别生活中遇到的各种二 ...

  5. 细说二维码扫码登录的原理

    前言 在日常生活中,二维码出现在很多场景,比如超市支付.系统登录.应用下载等等.了解二维码的原理,可以为技术人员在技术选型时提供新的思路.对于非技术人员呢,除了解惑,还可以引导他更好地辨别生活中遇到的 ...

  6. 聊聊二维码扫码登录的原理

    点击上方 "大数据肌肉猿"关注, 星标一起成长 后台回复[加群],进入高质量学习交流群 2021年大数据肌肉猿公众号奖励制度 原文:https://juejin.cn/post/6 ...

  7. 面试官:说说二维码扫码登录是什么原理吗?

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 juejin.cn/post/6940976355097985032 推荐:ht ...

  8. 二维码扫码登录的项目实战(建议收藏)

    点击上方[全栈开发者社区]→右上角[...]→[设为星标⭐] 点击领取全栈资料:全栈资料 前几天看了一个二维码的视频,写的不错,这里总结下. 在日常生活中,二维码出现在很多场景,比如超市支付.系统登录 ...

  9. 抖音二面:“聊聊二维码扫码登录的原理”

    文章来源:https://juejin.cn/post/6940976355097985032 目录 二维码登录的本质 认识二维码 系统认证机制 扫描二维码登录的一般步骤 总结 前言 在日常生活中,二 ...

最新文章

  1. VC++结束程序进程
  2. ssis foreach 使用ADO记录集
  3. Unity3d Http Get请求
  4. Qt IFW框架简介
  5. 给数值加上千分位的php程序,JavaScript_js 实现数值的千分位及保存小数方法(推荐),实例如下:/*** 将数 - phpStudy...
  6. Python -- post方式上传文件
  7. 前端学习(1569):todoMVC准备工作
  8. 2021年Q3小红书美妆行业营销报告
  9. JavaWeb EL表达式, JSTL标签及过滤器综合学习
  10. CSAPP,拆弹到一点!!!
  11. 优酷Android包瘦身治理思路
  12. 基于linux嵌入式浏览,基于LINUX的嵌入式浏览器的设计与 - 嵌入式操作系统 - 电子发烧友网...
  13. U3D Shader基础
  14. Java 交互小实例:(ATM机模拟、饮料自助机模拟)
  15. 菜鸟知识-五大智能手机操作系统
  16. VBA入门到进阶常用知识代码总结47
  17. 计算机考研838难吗,838初试经验教训分享
  18. 帝国cms中常用标签/灵动标签/判断语句
  19. 2022-2028全球丁二磺酸腺苷蛋氨酸行业调研及趋势分析报告
  20. Windows操作系统优化

热门文章

  1. 优优聚电商:商品标题优化的注意事项
  2. 被逼无奈(一)——5G NR小区搜索和同步
  3. python pip查看安装的包
  4. 专利被告知非正常申请,怎么办
  5. 程序员的路是一行一行走出来的
  6. 学前儿童计算机应用基础试卷,福建师范大学2020年2月课程考试《计算机应用基础》期末试卷A(在线考核).doc...
  7. es安装elasticsearch-head插件
  8. 李宏毅机器学习-explainable machine learning(机器学习的可解释性)及代码
  9. 大地测量计算工具集---长度变形估计
  10. python开发工程师工资_Python开发工程师工资一般多少钱