[电脑] 打开 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的我的博客