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

cyqv1737 8年前發布 | 31K 次閱讀

來自: http://my.oschina.net/myrainspace/blog/616427


微信掃描二維碼登錄網頁過程

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

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

  • [電腦] 手機掃描成功后,提示“登錄網頁版微信”;網頁上顯示“成功掃描 請在手機點擊確認以登錄”;

  • [手機] 手機端點擊“登錄網頁版微信”,網頁跳轉到用戶的微信操作界面;

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

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里面的信息)來請求用戶數據。

實例展示:

  • 發送輪詢請求,判斷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);
                });
            }
        }
        });
    }

微信掃描二維碼登錄網頁的總結

瀏覽器獲得一個臨時 id,通過長連接等待客戶端掃描帶有此 id 的二維碼后,從長連接中獲得客戶端上報給 server 的帳號信息進行展示。 并在客戶端點擊確認后,獲得服務器授信的令牌,進行隨后的信息交互過程。 在超時、網絡斷開、其他設備上登錄后,此前獲得的令牌或丟失、或失效,對授權過程形成有效的安全防護。


參考:

  1. 微信掃描二維碼登錄網頁是什么原理,前后兩個事件是如何聯系的?

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

 

 本文由用戶 cyqv1737 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!