微信QQ的二维码登录原理js代码解析


Posted in Javascript onJune 23, 2016

在很多地方就是都出现了使用二维码登录,二维码付款,二维码账户等应用(这里的二维码种马,诈骗就不说了),二维码验证,多终端辅助授权应用开始多起来,这里先说下啥是二维码,其实二维码就是存了二进制数据的黑白图片,当出现要求二维码登录的时候,服务器会生成一条临时的唯一的二维码信息,发送到客户端以二维码(图片)的形式写入到网页,然后你就会看到统一的四个方形的二维码,如果做的好这个二维码信息应该是有时效的,这里暂且不考虑这些,就简单的微信登录作为例子看看吧:

微信QQ的二维码登录原理js代码解析

首先说下整个授权流程:

微信QQ的二维码登录原理js代码解析

在客户端网页中会不断向服务器发送https连接,并且这里传输很少的数据之后就断开连接了,下面看下微信网页中这个login1c709c.js文件:

(function($, _aoWin) {

 _aoWin.QRLogin = {};
 _aoWin.LoginLog = "";
 var _sBaseHost = "",
 _oLoginQrCodeImg = document.getElementById("loginQrCode");
 if (document.domain == "qq.com") {
 _sBaseHost = "weixin.qq.com";
 } else if(location.hostname.match(/(wechat\.com)$/)){
 _sBaseHost = "wechat.com";
 }else{
 _sBaseHost = "wechatapp.com";
 }

 var show_tip = 1,
 _sCurUUId,
 _oResetTimeout,
 _aWebMMCallbacks = [],
 _oDetactWebMMInterval = setInterval(function(){
  if(_aoWin.WebMM){
  clearInterval(_oDetactWebMMInterval);
  var callback;
  while(callback = _aWebMMCallbacks.shift()){
   if(typeof(callback) != "function") continue;
   callback();
  }
  }
 }, 1000);

 function _logInPage(_asLog){
 _aoWin.LoginLog = LoginLog + _asLog + "\n";
 }

 function _afterLoadWebMMDo(callback){
 if(!_aoWin.WebMM){
  _aWebMMCallbacks.push(callback);
 }else{
  callback();
 }
 }

 function _reportNow(text){
 _logInPage(text);
 _afterLoadWebMMDo(function(){
  WebMM.ossLog({Text: text});
  WebMM.flushOssLog();
 });
 }

 var reLoadQRImgCount = 0,
 loadQRCodeTime = 0,
 loadQRImgSucc = function(){
  clearInterval(loadQRImgWatchDog);
  _logInPage("Load QRCode Success, time=" + (new Date().getTime() - loadQRCodeTime) + "ms, reload count: " + reLoadQRImgCount);
 },
 loadQRImgFail = function(img){
  _reportNow("Load QRcode fail!" + status + ", src: " + img.src + ", time: " + (new Date().getTime() - loadQRCodeTime) + "ms");
 },
 loadQRImgWatchDog = null;
 function _loadQRImg(uuid) {
 _poll(uuid);
 _logInPage("Load QRCode Start");
 loadQRCodeTime = new Date().getTime();

 _oLoginQrCodeImg.onload = function(){
  loadQRImgSucc();
  _oLoginQrCodeImg.onload = null;
 };
 _oLoginQrCodeImg.onerror = function(){loadQRImgFail(this)};
 _oLoginQrCodeImg.src = "https://login."+_sBaseHost+"/qrcode/"+uuid+"?t=webwx";

 loadQRImgWatchDog = setInterval(function(){
  if (reLoadQRImgCount >= 5) {
  _reset();
  return;
  }
  reLoadQRImgCount++;

  var _img = new Image();
  _img.onload = function () {
  if(!_oLoginQrCodeImg.onload) return;

  _oLoginQrCodeImg.onload = null;
  _oLoginQrCodeImg.src = this.src;//replace
  loadQRImgSucc();
  };
  _img.onerror = function(){loadQRImgFail(this)};
  _img.src = _oLoginQrCodeImg.src + "&r=" + new Date().getTime();
 }, 5000);
 }

 var _sSecondRequestTime = 0,
 _nAjaxTimeout = 100 * 1000,
 _nNewLoginFuncErrCount = 0;
 function _poll(_asUUID) {
 var _self = arguments.callee,
  _nTime = 0;
 _sCurUUId = _asUUID;

 _logInPage("_poll Request Start, time: " + new Date().getTime());
 _nTime = new Date().getTime();
 $.ajax({
 type: "GET",
 url: "https://login." + _sBaseHost + "/cgi-bin/mmwebwx-bin/login?uuid=" + _asUUID + "&tip=" + show_tip,
 dataType: "script",
 cache: false,
 timeout: _nAjaxTimeout,
 success: function(data, textStatus, jqXHR) {
  _logInPage("_poll Request Success, code: " + window.code + ", time: " + (new Date().getTime() - _nTime) + "ms");
 switch (_aoWin.code) {
 case 200:
  _sSecondRequestTime = new Date().getTime() - _sSecondRequestTime;
  _logInPage("Second Request Success, time: " + _sSecondRequestTime + "ms");
 clearTimeout(_oResetTimeout);

  var _fNewLoginFunc = function(){
   $.ajax({
   url: _aoWin.redirect_uri + "&fun=new",//new login page
   type: "GET",
   success:function(msg) {
    _logInPage("new func reponse, reponseMsg: " + msg);
    var code = msg.match(/<script>(.*)<\/script>/);
    var skey=msg.match(/<skey>(.*)<\/skey>/);
    if(code){
    eval(code[1]);
    }else{
    $("#container").show();
    $("#login_container").hide();
    }
    if(skey && skey[1]){
    WebMM.model("account").setSkey(skey[1]);
    }
   },
   error:function(jqXHR, textStatus, errorThrown){
    _nNewLoginFuncErrCount++;
    if(_nNewLoginFuncErrCount > 5){
    if(confirm("Call new login page func error, refresh?")){location.reload()}
    return;
    }
    _reportNow(_aoWin.redirect_uri + " New login page func error: " + textStatus +" retryCount:" + _nNewLoginFuncErrCount);
    setTimeout(_fNewLoginFunc, 500);
   }
   });
  };
  _fNewLoginFunc();

  _reportNow("/cgi-bin/mmwebwx-bin/login, Second Request Success, uuid: " + _asUUID + ", time: " + _sSecondRequestTime + "ms");
 break;

 case 201:
  clearTimeout(_oResetTimeout);
 show_tip = 0;
 $('.errorMsg').hide();
 $('.normlDesc').hide();
 $('.successMsg').show();
  _reportNow("/cgi-bin/mmwebwx-bin/login, First Request Success, uuid: " + _asUUID);
  _reportNow("/cgi-bin/mmwebwx-bin/login, Second Request Start, uuid: " + _asUUID);

  _sSecondRequestTime = new Date().getTime();

  //_nAjaxTimeout = 5 * 1000;
  _self(_asUUID);
  break;

 case 408:
 setTimeout(function(){
 _self(_asUUID);
 }, 500);
 break;

 case 400:
 case 500:
  _reset();
  _afterLoadWebMMDo(function(){
 _aoWin.Log.d("500, Login Poll Svr Exception");
 });
 break;
 }
 },
 error: function(jqXHR, textStatus, errorThrown) {
 if (textStatus == 'timeout') {
  setTimeout(function(){
   _self(_asUUID);
  }, 500);
 } else {
  setTimeout(function(){
   _self(_asUUID);
  }, 5000);

  _logInPage("_poll Request Error:" + textStatus);
  _afterLoadWebMMDo(function(){
   _aoWin.Log.e("Login Poll Error:" + textStatus);
  });
 }
 }
 });
 }

 var getUUIDCount = 0,
 _getUUIDWatchDog,
 _bGetUUIDSuccess = false;//ajax successִ
 function _getUUID() {
 getUUIDCount++;
 var _self = arguments.callee,
  _loadError = function(errorText){
  _reportNow("Load UUID Error! ErrorText: " + errorText + " getUUIDCount=" + getUUIDCount);
  if(getUUIDCount > 5){
   if (confirm("Load uuid error. Refresh?")) {
   location.reload();
   }
  }
  setTimeout(function(){
   _self();
  }, 500);
  };

 clearTimeout(_getUUIDWatchDog);
 _getUUIDWatchDog = setTimeout(function(){
  if(!_aoWin.QRLogin.code){
  _logInPage("GetUUID Timeout, WatchDog Run");
  _self();
  }
 }, 10000);

 $.ajax({
  type: "GET",
  url: "https://login." + _sBaseHost + "/jslogin?appid=wx782c26e4c19acffb&redirect_uri="+encodeURIComponent(location.protocol+"//"+location.host+"/cgi-bin/mmwebwx-bin/webwxnewloginpage")+"&fun=new&lang=" + document.lang,
  dataType: "script",
  cache: false,
  success : function(){
  clearTimeout(_getUUIDWatchDog);
  if(_bGetUUIDSuccess) return;
  if (_aoWin.QRLogin && _aoWin.QRLogin.code == 200) {
   _logInPage("GetUUID Success, UUID=" + QRLogin.uuid);
   _bGetUUIDSuccess = true;

   clearTimeout(_oResetTimeout);
   _oResetTimeout = setTimeout(function(){
   location.reload();//Note: Don't run _reset(). If you run _reset(), there will may have many _poll request, as they get 408 return code
   }, 5 * 60 *1000);//5 mins

   _loadQRImg(QRLogin.uuid);
  } else {
   var QRLoginCode = (_aoWin.QRLogin && _aoWin.QRLogin.code) ? _aoWin.QRLogin.code : "None";
   _logInPage("GetUUID Error, QRLogin.code=" + QRLoginCode);
   _loadError("QRLogin.code= " + QRLoginCode);
  }
  },
  error : function(xhr, textStatus, errorThrown){
  _logInPage("GetUUID Error, textStatus=" + textStatus);
  _loadError(textStatus);
  }
 });
 }

 function _reset(){
 location.reload();
 }

 if ($("#login_container").is(":visible") ) {
 _getUUID();
 }

 
 var _bHadLog = false;
 function _ossLog() {
 if (_bHadLog) return;
 _bHadLog = true;
 var _sUvid = document.cookie.match(new RegExp( "(^| )"+"webwxuvid"+"=([^;]*)(;|$)"));
 if(!_sUvid || _sUvid.length < 3) return;
 _sUvid = _sUvid[2];
 (new Image()).src = "/cgi-bin/mmwebwx-bin/webwxstatreport?funkey=indexdemo&uvid="+_sUvid+"&uuid="+_sCurUUId;
 }


 if($("img.guide").length > 0) {
 var _nTimer = 0,
 _oGuide$ = $(".guide"),
 _oGuideTrigger$ = $("#guideTrigger, #tipTrigger"),
 _oMask$ = $(".mask");

 function _back() {
 _nTimer = setTimeout(function() {
 _oMask$.stop().animate({opacity:0}, function(){$(".mask").hide()});
 _oGuide$.stop().animate({marginLeft:"-120px",opacity:0}, "400", "swing",function(){
 _oGuide$.hide();
 });
 }, 100);
 }

 /*guide*/
 _oGuide$.css({"left":"50%", "opacity":0});
 _oGuideTrigger$.css({"backgroundColor":"white", "opacity":"0"});
 _oGuideTrigger$.mouseover(function(){
 clearTimeout(_nTimer);
 _oMask$.show().stop().animate({"opacity":0.2});
 _oGuide$.css("display", "block").stop().animate({marginLeft:"+168px", opacity:1}, 900, "swing", function() {
 _oGuide$.animate({marginLeft:"+153px"}, 300);
 });
 _ossLog();
 }).mouseout(_back);

 _oGuide$.mouseover(function(){
 clearTimeout(_nTimer);
 }).mouseout(_back);
 }
})(jQuery, window);

细读js之后,你就会从网页客户端这边看到请求登录的一面,网页客户端每隔500毫秒就向服务器发起ssl请求,请求当前的二维码是否被其他客户端(手机)授权,如果返回结果是201,就是说明已经获取扫描二维码终端相同的账号登录授权,如果是其他情况就再隔500毫秒再循环发请求。这个过程会一直持续到二维码被扫描通过或者二维码超时(失效)为止。
 其中使用的工具有: 抓包工具 Fidller ,Chrome F12开发人员工具,注意偶然的发现,微信的客户端有一个min-webmm1cba21.js ,其中清晰可见的XSS filter规范, 这对于那些喜欢白盒测试XSS的鸽子又有希望拿Q仔了!!!

本文已被整理到了《JavaScript微信开发技巧汇总》,欢迎大家学习阅读。

为大家推荐现在关注度比较高的微信小程序教程一篇:《微信小程序开发教程》小编为大家精心整理的,希望喜欢。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
告诉大家什么是JSON
Jun 10 Javascript
jQuery表单验证插件formValidator(改进版)
Feb 03 Javascript
js中settimeout方法加参数
Feb 28 Javascript
javascript校验价格合法性实例(必须输入2位小数)
May 05 Javascript
js比较日期大小的方法
May 12 Javascript
Jquery+Ajax+PHP+MySQL实现分类列表管理(下)
Oct 28 Javascript
jQuery动画效果相关方法实例分析
Dec 31 Javascript
AngularJS手动表单验证
Feb 01 Javascript
解决vuecli3.0热更新失效的问题
Sep 19 Javascript
详解如何构建Promise队列实现异步函数顺序执行
Oct 23 Javascript
微信小程序点击保存图片到本机功能
Dec 13 Javascript
JavaScript常用进制转换及位运算实例解析
Oct 14 Javascript
再次谈论Javascript中的this
Jun 23 #Javascript
BootStrap使用popover插件实现鼠标经过显示并保持显示框
Jun 23 #Javascript
Bootstrap导航条可点击和鼠标悬停显示下拉菜单的实现代码
Jun 23 #Javascript
Bootstrap弹出带合法性检查的登录框实例代码【推荐】
Jun 23 #Javascript
JS留言功能的简单实现案例(推荐)
Jun 23 #Javascript
Bootstrap实现登录校验表单(带验证码)
Jun 23 #Javascript
JavaScript自学笔记(必看篇)
Jun 23 #Javascript
You might like
星际争霸 Starcraft 秘技补丁
2020/03/14 星际争霸
PHP编程之高级技巧——利用Mysql函数
2006/10/09 PHP
AMFPHP php远程调用(RPC, Remote Procedure Call)工具 快速入门教程
2010/05/10 PHP
php获取当前网址url并替换参数或网址的方法
2010/06/06 PHP
PHP UTF8中文字符截断函数代码
2012/09/11 PHP
PHP中的一些常用函数收集
2015/05/26 PHP
PHP实现表单提交数据的验证处理功能【防SQL注入和XSS攻击等】
2017/07/21 PHP
PHP实现的基于单向链表解决约瑟夫环问题示例
2017/09/30 PHP
Windows Live的@live.com域名注册漏洞 利用代码
2006/12/27 Javascript
js字符串转换成xml对象并使用技巧解读
2013/04/18 Javascript
5秒后跳转效果(setInterval/SetTimeOut)
2013/05/03 Javascript
使用vue.js制作分页组件
2016/06/27 Javascript
利用Javascript实现BMI计算器
2016/08/16 Javascript
原生Aajax 和jQuery Ajax 写法个人总结
2017/03/24 jQuery
Angularjs上传文件组件flowjs功能
2017/08/07 Javascript
微信小程序自定义tab实现多层tab嵌套功能
2018/06/15 Javascript
vue引用外部JS的两种种方法
2020/01/28 Javascript
Vue的Eslint配置文件eslintrc.js说明与规则介绍
2020/02/03 Javascript
vue v-for 点击当前行,获取当前行数据及event当前事件对象的操作
2020/09/10 Javascript
nodejs处理tcp连接的核心流程
2021/02/26 NodeJs
python MySQLdb Windows下安装教程及问题解决方法
2015/05/09 Python
详解使用 pyenv 管理多个版本 python 环境
2017/10/19 Python
解决Mac安装scrapy失败的问题
2018/06/13 Python
浅谈Python3中strip()、lstrip()、rstrip()用法详解
2019/04/29 Python
python打包exe开机自动启动的实例(windows)
2019/06/28 Python
Python实现制度转换(货币,温度,长度)
2019/07/14 Python
Python容器使用的5个技巧和2个误区总结
2019/09/26 Python
python栈的基本定义与使用方法示例【初始化、赋值、入栈、出栈等】
2019/10/24 Python
使用Python完成15位18位身份证的互转功能
2019/11/06 Python
安装PyInstaller失败问题解决
2019/12/14 Python
Python requests设置代理的方法步骤
2020/02/23 Python
通过代码实例解析Pytest运行流程
2020/08/20 Python
优秀学生党员先进事迹材料
2014/05/29 职场文书
护士求职信
2014/07/05 职场文书
县政府办公室领导班子个人对照检查材料
2014/09/16 职场文书
2015年维修电工工作总结
2015/04/25 职场文书