[電腦] 打開 http://wx.qq.com,獲得二維碼;web
[手機] 手機登陸微信,點開掃一掃,掃描PC端二維碼,而且掃描成功;ajax
[電腦] 手機掃描成功後,提示「登陸網頁版微信」;網頁上顯示「成功掃描 請在手機點擊確認以登陸」;瀏覽器
[手機] 手機端點擊「登陸網頁版微信」,網頁跳轉到用戶的微信操做界面;安全
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
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 的賬號信息進行展現。 並在客戶端點擊確認後,得到服務器授信的令牌,進行隨後的信息交互過程。 在超時、網絡斷開、其餘設備上登陸後,此前得到的令牌或丟失、或失效,對受權過程造成有效的安全防禦。網絡
參考:ide
轉載時請註明:來自w-rain的我的博客