微信掃描二維碼登陸網頁的原理

1.微信掃描二維碼登陸網頁過程

  • [電腦] 打開 http://wx.qq.com,獲得二維碼;web

  • [手機] 手機登陸微信,點開掃一掃,掃描PC端二維碼,而且掃描成功;ajax

  • [電腦] 手機掃描成功後,提示「登陸網頁版微信」;網頁上顯示「成功掃描 請在手機點擊確認以登陸」;瀏覽器

  • [手機] 手機端點擊「登陸網頁版微信」,網頁跳轉到用戶的微信操做界面;安全

2.微信掃描二維碼登陸網頁的原理

1.每次打開微信網頁版的時候,都會生成一個含有惟一uid的二維碼,並且每次刷新後都會改變。這樣能夠保證一個uid只能夠綁定一個帳號和密碼,肯定登陸用戶的惟一性。能夠經過手機上的UC瀏覽器提供的掃碼功能查看二維碼裏面的信息,但並不會自動打開該地址,微信客戶端針對 http://weixin.qq.com/x/ 開頭的地址作了特殊處理,會自動獲取相關信息並提示確認。 在手機版微信訪問這個頁面進行確認時,Server已經同時得到了客戶端信息,並經過以前保持的長鏈接告知瀏覽器。返回的惟一 id,目的是爲了識別用戶身份,並且實際上打開這個頁面的時候瀏覽器已經和 Server 建立了一個長鏈接等待確認信息。查看 http://wx.qq.com 的源碼能夠看到,這個頁面在加載完畢時,也已經把不少登陸後才須要的相關資源都預先加載進來了,因此長鏈接等待登陸用戶獲得確認後展現用戶信息的速度很快,由於無需刷頁面和加載頭像外的其餘資源。服務器

2. 在網頁生成這個二維碼的時候,網頁就開始用ajax長輪詢,對服務器請求這個UID的掃描記錄,若是沒有,在特定時長後(目前是27秒左右)會接到狀態碼408,表示應該繼續下一次請求,若是獲得狀態碼201後,通知服務器,客戶端由此也進入一個新的頁面(就是那個要你點確認的按鈕),原理跟上一步相同(長輪詢)。這個時候你只要點擊確認,服務器就開始給該客戶端的用戶進行自動登陸,並把用戶信息在這一步經過當前的某個上行的長輪詢給返回出去。固然返回的方式再也不是什麼狀態碼了,而是header裏面的Set-Cookie,其實內容其實也至關於狀態碼:<error><ret>0</ret><message>OK</message><skey>xxxx</skey></error>。微信

3. 瀏覽器就能夠成功地用微信承認的任何一種認證方式(經過返回的skey和cookie裏面的信息)來請求用戶數據。cookie

實例展現:

 

  •  

  • 發送輪詢請求,判斷uuid是否綁定了用戶的登錄簽名

若是30秒內用戶未掃碼,uuid未綁定用戶的登錄簽名,則後臺返回結果碼 window.code=408

 

  • 微信客戶端請求信息

  •  

  • 掃碼成功界面

長輪詢代碼:

    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);
 
                $.get(_aoWin.redirect_uri + "&fun=new", function(msg) {
                    _logInPage("new func reponse, reponseMsg: " + msg);
                    _reportNow("new func reponse, reponseMsg: " + msg);
                    var code = msg.match(/<script>(.*)<\/script>/);
                    if(code){
                        eval(code[1]);
                    }else{
                        $("#container").show();
                        $("#login_container").hide();
                    }
                });
 
                _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();
                _logInPage("First Request Success");
                _reportNow("/cgi-bin/mmwebwx-bin/login, First Request Success, uuid: " + _asUUID);
//                setTimeout(function(){
                    _logInPage("Second Request Start");
                    _reportNow("/cgi-bin/mmwebwx-bin/login, Second Request Start, uuid: " + _asUUID);
 
                    _sSecondRequestTime = new Date().getTime();
 
                    _nAjaxTimeout = 5 * 1000;
                    _self(_asUUID);
//                }, 500);
                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);
                });
            }
        }
        });
    }

 

3.微信掃描二維碼登陸網頁的總結

瀏覽器得到一個臨時 id,經過長鏈接等待客戶端掃描帶有此 id 的二維碼後,從長鏈接中得到客戶端上報給 server 的賬號信息進行展現。 並在客戶端點擊確認後,得到服務器授信的令牌,進行隨後的信息交互過程。 在超時、網絡斷開、其餘設備上登陸後,此前得到的令牌或丟失、或失效,對受權過程造成有效的安全防禦。網絡

 

參考:ide

  1. 微信掃描二維碼登陸網頁是什麼原理,先後兩個事件是如何聯繫的?ui

  2. 微信掃碼登陸網頁實現原理

 

轉載時請註明:來自w-rain的我的博客

相關文章
相關標籤/搜索