溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

移動(dòng)開發(fā)之實(shí)現(xiàn)微信QQ二維碼登錄原理的示例分析

發(fā)布時(shí)間:2021-07-27 11:40:33 來源:億速云 閱讀:183 作者:小新 欄目:移動(dòng)開發(fā)

這篇文章主要介紹移動(dòng)開發(fā)之實(shí)現(xiàn)微信QQ二維碼登錄原理的示例分析,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

在很多地方就是都出現(xiàn)了使用二維碼登錄,二維碼付款,二維碼賬戶等應(yīng)用(這里的二維碼種馬,詐騙就不說了),二維碼驗(yàn)證,多終端輔助授權(quán)應(yīng)用開始多起來,這里先說下啥是二維碼,其實(shí)二維碼就是存了二進(jìn)制數(shù)據(jù)的黑白圖片,當(dāng)出現(xiàn)要求二維碼登錄的時(shí)候,服務(wù)器會(huì)生成一條臨時(shí)的唯一的二維碼信息,發(fā)送到客戶端以二維碼(圖片)的形式寫入到網(wǎng)頁(yè),然后你就會(huì)看到統(tǒng)一的四個(gè)方形的二維碼,如果做的好這個(gè)二維碼信息應(yīng)該是有時(shí)效的,這里暫且不考慮這些,就簡(jiǎn)單的微信登錄作為例子看看吧:

移動(dòng)開發(fā)之實(shí)現(xiàn)微信QQ二維碼登錄原理的示例分析

首先說下整個(gè)授權(quán)流程

移動(dòng)開發(fā)之實(shí)現(xiàn)微信QQ二維碼登錄原理的示例分析

在客戶端網(wǎng)頁(yè)中會(huì)不斷向服務(wù)器發(fā)送https連接,并且這里傳輸很少的數(shù)據(jù)之后就斷開連接了,下面看下微信網(wǎng)頁(yè)中這個(gè)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);

細(xì)讀js之后,你就會(huì)從網(wǎng)頁(yè)客戶端這邊看到請(qǐng)求登錄的一面,網(wǎng)頁(yè)客戶端每隔500毫秒就向服務(wù)器發(fā)起ssl請(qǐng)求,請(qǐng)求當(dāng)前的二維碼是否被其他客戶端(手機(jī))授權(quán),如果返回結(jié)果是201,就是說明已經(jīng)獲取掃描二維碼終端相同的賬號(hào)登錄授權(quán),如果是其他情況就再隔500毫秒再循環(huán)發(fā)請(qǐng)求。這個(gè)過程會(huì)一直持續(xù)到二維碼被掃描通過或者二維碼超時(shí)(失效)為止。
其中使用的工具有: 抓包工具 Fidller ,Chrome F12開發(fā)人員工具,注意偶然的發(fā)現(xiàn),微信的客戶端有一個(gè)min-webmm1cba21.js ,其中清晰可見的XSS filter規(guī)范, 這對(duì)于那些喜歡白盒測(cè)試XSS的鴿子又有希望拿Q仔了?。?!

以上是“移動(dòng)開發(fā)之實(shí)現(xiàn)微信QQ二維碼登錄原理的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI