>  기사  >  위챗 애플릿  >  QR코드 로그인 원리에 대한 자세한 설명

QR코드 로그인 원리에 대한 자세한 설명

Y2J
Y2J원래의
2017-05-16 11:21:363159검색

이 글은 주로 WeChat QQ의 QR코드 로그인 원리를 자세히 분석한 내용입니다. 관심있는 친구들이 참고하시면 좋을 것 같습니다. QR 코드 결제, QR 코드 계정 및 기타 응용 프로그램(여기서 QR 코드 종마, 사기는 언급하지 않음), QR 코드 확인, 다중 단말기 보조 인증 응용 프로그램이 증가하기 시작했습니다. 먼저 QR 코드란 무엇입니까? 실제로 QR코드는 바이너리 데이터를 저장하는 흑백

사진

으로, 로그인을 위해 QR코드를 요청하면 서버에서 임시적이고 고유한 QR코드 메시지를 생성해 전송한다. 클라이언트에 QR 코드(사진) 형식으로 웹 페이지에 작성하면 통일된 4각형 QR 코드가 표시됩니다. 잘 수행되면 QR 코드 정보는 당분간 시간에 민감해야 합니다. , 이에 관계없이 간단한 WeChat 로그인을 예로 들어 보겠습니다.

먼저 전체 인증 프로세스에 대해 이야기하겠습니다.

클라이언트 웹페이지에서는 https 연결이 지속적으로 서버로 전송되고, 아주 적은 양의 데이터만 전송된 후 연결이 끊어집니다. WeChat 웹페이지의 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;
 $(&#39;.errorMsg&#39;).hide();
 $(&#39;.normlDesc&#39;).hide();
 $(&#39;.successMsg&#39;).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 == &#39;timeout&#39;) {
  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&#39;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 요청을 시작하여 현재 QR 코드가 다른 클라이언트(휴대폰)에서 인증되는지 요청합니다. 반환 결과는 201이며, 이는 QR 코드를 스캔한 단말기와 동일한 계정에 대한 로그인 인증을 획득했다는 의미입니다. 다른 상황이라면 500밀리초를 더 기다린 후

루프

하여 전송하세요. 요구. 이 프로세스는 QR 코드가 스캔되거나 QR 코드가 시간 초과(잘못됨)될 때까지 계속됩니다. 【관련 추천】
1.

특별 추천: "php Programmer Toolbox" V0.1 버전 다운로드2.

WeChat 미니 프로그램 전체 소스 코드 다운로드

3.

WeChat 미니 프로그램 데모: NetEase Cloud Music 모방

위 내용은 QR코드 로그인 원리에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.