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

前端技术 2023/09/05 JavaScript

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

首先说下整个授权流程:

在客户端网页中会不断向服务器发送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〈=\" + 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仔了!!!

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

本文地址:https://www.stayed.cn/item/12955

转载请注明出处。

本站部分内容来源于网络,如侵犯到您的权益,请 联系我

我的博客

人生若只如初见,何事秋风悲画扇。