在很多地方就是都出現了使用二維碼登錄,二維碼付款,二維碼帳戶等應用(這裡的二維碼種馬,詐騙就不說了),二維碼驗證,多終端輔助授權應用程式開始多起來,這裡先說下啥是二維碼,其實二維碼就是存了二進位資料的黑白圖片,當出現要求二維碼登入的時候,伺服器會產生一條臨時的唯一的二維碼訊息,發送到客戶端以二維碼(圖片)的形式寫入到網頁,然後你會看到統一的四個方形的二維碼,如果做的好這個二維碼信息應該是有時效的,這裡暫且不考慮這些,就簡單的微信登錄作為例子看看吧:
首先說下整個授權流程
在客戶端網頁中會不斷向服務器發送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仔了! ! !
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持PHP中文網。
更多微信QQ的二維碼登入原理js程式碼解析相關文章請關注PHP中文網!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

Dreamweaver CS6
視覺化網頁開發工具

Atom編輯器mac版下載
最受歡迎的的開源編輯器

禪工作室 13.0.1
強大的PHP整合開發環境

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中