初涉扫码登录:edusoho实现客户端扫码登录(简版)
Posted 黄小涛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初涉扫码登录:edusoho实现客户端扫码登录(简版)相关的知识,希望对你有一定的参考价值。
一、项目简介及需求
edusoho是一套商业版的在线教育平台,项目本身基于symfony2框架开发,现在有一款自己的APP,要求在不多修改edusoho自身代码的基础上,实现客户端对PC端扫码登录。不多修改edusoho代码的原因是为了避免下次升级主版本时发生错误。
二、版本信息及所需应用
edusoho 7.5.14
php 5.5.25
php GD库
memcache(本次使用memcache作为存储介质,可用redis代替) 点我下载
phpqrcde(php生成二维码插件)
三、实现思路
点击页面扫码按钮,向php发送一个请求,php生成二维码并把sign验证字符串保存至memcache中,请求成功后,页面展示二维码等待扫码,并使用ajax以轮询的方式请求后台方法,查询是否已扫码。手机扫码后访问二维码里面的url链接,php保存已扫码标识,并向ajax返回已扫码状态,前台页面提示已扫码,并再次发送是否已确认登录的ajax轮询。扫码完成后,APP端显示是否登录页面,点击确认,向后台方法传递用户名及sign验证字符串,后台验证通过后,返回用户名,sign及状态码至前端ajax。
由于需求是不多改动edusoho自身代码,所以登录时借用edusoho本身的登录,即把ajax返回的用户名填写进用户名框,sign填进密码框,触发登录的submit按钮,只在登录的密码验证处添加了几行代码,把密码验证,改为了sign验证。(symfony2采用的登录方式是security配置化登录,后面会讲到在哪里改动密码验证,不熟悉的可以参考 security安全登录)
四、实现代码及步骤
1、生成登录二维码
(1)先在custom下的routing.yml配置访问路由(其他方式路由请自行参考配置,以下方法的路由配置不再赘述)
(2)点击扫码按钮的js代码(可以把key存进cookie中,本示例直接写进了页面的隐藏域)
//点击扫码弹出框 $(\'.scanqrcode\').click(function (){ $(\'.login-section\').hide();//隐藏登陆框 $(\'.qrcode\').show();//弹窗 $(\'.timeout\').hide(); $(\'.barcode-container.scanned .status.scanned, .barcode-container.scanned .mask\').hide(); $(\'.login_op\').show();//显示遮罩层 //请求二维码 $.ajax({ type: "POST", dataType: "json", url: "/login/create/qrcode", success: function (data) { if(data.status==1){ var qrcodeimg = \'../../assets/img/qrcodeimg/\'+data.msg+\'.png\'; //把key放进隐藏域 $(\'#key\').val(data.msg); //替换二维码 $(\'.qrcode-img\').attr(\'src\',qrcodeimg); //触发定时任务,查看是否已扫码 var inter = setInterval("is_sacn_qrcode();",3000); $(\'#timing\').val(inter); } } }); });
(3)写createQrcode方法生成二维码图片的php代码(doString方法是生成sign字符串的算法,还请自行发明创造,生成结果每次都不一样是最好的)
//生成登录二维码 public function CreateQrcodeAction(){ ob_start(); $url = \'http://\'.$_SERVER[\'HTTP_HOST\'];//获取当前的url $http = $url.\'/login/mobile/scan/qrcode\';//确认扫码的url方法 $key = $this->getRandom(30);//存放在memcache中的键值,随机32位字符串 $_SESSION[\'qrcode_name\'] = $key;//把key当做图片的名字存在session里 $sgin_data = $this->doString();//生成sign字符串的基本算法 $sgin = strrev(substr($key,0,2)) . $sgin_data;//截取前两位并反转 $value = $http.\'?key=\'.$key.\'&type=1\';//二维码内容 $errorCorrectionLevel = \'H\';//容错级别 $matrixPointSize = 8;//生成图片大小 //生成二维码图片 \\QRcode::png($value, \'qrcode.png\', $errorCorrectionLevel, $matrixPointSize, 0); $logo = "assets/img/qrcodeimg_logo.png";//准备好的logo图片 $QR = \'qrcode.png\';//已经生成的原始二维码图 if ($logo !== FALSE) { $QR = imagecreatefromstring(file_get_contents($QR)); $logo = imagecreatefromstring(file_get_contents($logo)); $QR_width = imagesx($QR);//二维码图片宽度 $QR_height = imagesy($QR);//二维码图片高度 $logo_width = imagesx($logo);//logo图片宽度 $logo_height = imagesy($logo);//logo图片高度 $logo_qr_width = $QR_width / 3; $scale = $logo_width/$logo_qr_width; $logo_qr_height = $logo_height/$scale; $from_width = ($QR_width - $logo_qr_width) / 2; //重新组合图片并调整大小 imagecopyresampled($QR, $logo, $from_width, $from_width, 0, 0, $logo_qr_width, $logo_qr_height, $logo_width, $logo_height); } //输出图片 $img = imagepng($QR, \'assets/img/qrcodeimg/\'.$key.\'.png\'); $return = array(\'status\'=>0,\'msg\'=>\'\'); if($img){ $mem = new \\Memcache(); $mem->connect(\'127.0.0.1\',11211); $res = json_encode(array(\'sign\'=>$sgin,\'type\'=>0)); //存进memcache,过期时间三分钟 $mem->set($key,$res,0,180);//180 $return = array(\'status\'=>1,\'msg\'=>$key); } return $this->createJsonResponse($return); }
2、jquery弹出页面二维码并启动ajax轮询查询是否扫码
(1)显示效果图:
(2)请求是否扫码的js代码(未扫码就一直轮询,已扫码关闭“查看是否已扫码”方法,开启“查看是否确认登录”的方法轮询,关闭二维码框清除所有方法)
//查看是否已扫码 function is_sacn_qrcode (){ $.ajax({ type: "POST", dataType: "json", url: " /login/scan/qrcode", success: function (data) { if(data.status==1){ //扫码成功 $(\'.barcode-container.scanned .status.scanned, .barcode-container.scanned .mask\').show(); //取消定时任务,清除cookie clearInterval($(\'#timing\').val()); $(\'#timing\').val(\'\'); ////定时2秒关闭弹窗 //setTimeout(function(){ // $(\'.qrcode\').hide(); //},2000); //查看是否已确认登录 var is_login = setInterval("is_login();",3000); $(\'#is_login\').val(is_login); //$.cookie(\'is_login\', is_login); }else if(data.status==2){ $(\'.timeout,.mask\').show(); //取消定时任务,清除cookie clearInterval($(\'#timing\').val()); $(\'#timing\').val(\'\'); } } }); }
(3)查看是否已扫码的php代码
/** * 查看是否已扫码 */ public function isScanQrcodeAction(){ $key = $_SESSION[\'qrcode_name\']; $mem = new \\Memcache(); $mem->connect(\'127.0.0.1\',11211); $data = json_decode($mem->get($key),true); if(empty($data)){ $return = array(\'status\'=>2,\'msg\'=>\'已过期\'); }else{ if($data[\'type\']){ $return = array(\'status\'=>1,\'msg\'=>\'成功\'); }else{ $return = array(\'status\'=>0,\'msg\'=>\'\'); } } return $this->createJsonResponse($return); }
(4)客户端扫码的php代码
//移动设备扫码 public function mobileScanQrcodeAction(Request $request,$key){ $key = $_GET[\'key\']; $url = \'http://\'.$_SERVER[\'HTTP_HOST\']; $agent=$_SERVER["HTTP_USER_AGENT"]; if (!(strpos($agent, \'MicroMessenger\') === false)) { // 获取版本号 //preg_match(\'/.*?(MicroMessenger\\/([0-9.]+))\\s*/\', $agent, $matches); $app_url = \'http://club.risecenter.com/wap_app.html\'; // 微信浏览器,跳转至下载APP页面(可判断非指定app浏览器都跳转至此页面) return $this->redirect($app_url); } $http = $url.\'/login/qrcodedoLogin\';//返回确认登录的链接 $mem = new \\Memcache(); $mem->connect(\'127.0.0.1\',11211); $data = json_decode($mem->get($key),true); $data[\'type\']=1;//增加type值,用来判断是否已扫码 $res = json_encode($data); $mem->set($key,$res,0,180); $http = $http.\'?key=\'.$key.\'&type=scan\'; $return = array(\'status\'=>1,\'msg\'=>$http); return $this->createJsonResponse($return); }
3、扫码成功后判断是否确认登录
(1)扫码成功效果图:
(2)扫码成功后查询是否登录js代码(客户端确认登录后把用户名传递给ajax,js把用户名和sign填到用户名和密码表单,触发页面隐藏的submit登录按钮)
//查看是否已确认登录 function is_login(){ var key = $(\'#key\').val(); $.ajax({ type: "POST", dataType: "json", url: "/login/entry/login", data:{ key:key }, success: function (data) { if(data.status==1){ var uid = data.uid; var sign = data.sign; //取消定时任务,清除cookie clearInterval($(\'#is_login\').val()); $(\'#is_login\').val(\'\'); //隐藏扫码成功 $(\'.barcode-container.scanned .status.scanned, .barcode-container.scanned .mask\').hide(); //弹出已确认 $(\'.confirmed,.mask\').show(); //定时1秒确认登陆 setTimeout(function(){ //确认登录 $(\'#login_username\').val(data.user); $(\'#login_password\').val(data.sign); $(\'#login-form\').submit(); },1000); }else if(data.status==2){ //取消定时任务,清除cookie clearInterval($(\'#is_login\').val()); $(\'#is_login\').val(\'\'); alert(data.msg); } } }); }
(3)查询是否已确认登录的php代码
/** * 客户端扫码后登录 * $sign 客户端扫码时传递标识,与memcache中的做对比 * $key 网页端二维码中传递的key * $uid 客户端登陆后扫码传递的用户id * @return void */ public function qrcodeDoLoginAction(Request $request,$login,$key,$sign){ $login = $_GET[\'login\']; $key = $_GET[\'key\']; $sign = $_GET[\'sign\']; $mem = new \\Memcache(); $mem->connect(\'127.0.0.1\',11211); $data = json_decode($mem->get($key),true);//取出memcache的值 if($data[\'sign\']!=$sign){//验证传递的sign $return = array(\'status\'=>0,\'msg\'=>\'验证错误\'); return $this->createJsonResponse($return); }else{ if($login){//手机扫码网页登陆,把用户名存进memcache $data[\'login\'] = $login; $res = json_encode($data); $mem->set($key,$res,0,180); $return = array(\'status\'=>1,\'msg\'=>\'登录成功\'); return $this->createJsonResponse($return); }else{ $return = array(\'status\'=>0,\'msg\'=>\'请传递正确的用户信息\'); return $this->createJsonResponse($return); } } }
4、确认登录
走到这里,就到了本次扫码登录的最后一步了,也是最关键的一步,不知道诸位看官们把symfony2的security安全登录机制看的怎么样了,原理先不管了,毕竟不在本次的讨论范围之内,直接说改哪好了。
/src/topxia/WebBundle/Handler/AuthenticationProvider.php 的checkAuthentication方法,可以直接改,也可以继承到custom再改。
php代码如下:
5、二维码过期设置
为了安全考虑,设置二维码过期是很关键的一个步骤,在所有的php代码里,存放在memcache中的数据都有一个时间限制,本示例中的时间是3分钟,过期后,memcache会删除掉原有的数据记录,当ajax请求不到数据的时候,要在页面显示二维码已过期,要求重新刷新二维码。
效果图如下:
结论:
1、php扫码登录只是一个确认的过程,在每次访问接口的时候,安全验证尤为重要,本次方法未涉及到验证的算法,请诸位根据自身项目进行补充调整,先有理念再说嘛。
2、对于ajax轮询的方法是否low,嗯,low。还有更好的实现方式,比如websocket,goeasy等大家见仁见智,不过貌似支付宝和京东的扫码都是轮询,不对请见谅。
3、本次扫码登录只是一个理念,不仅仅针对edusoho平台,所有的都可以移植过去使用,不过,做好安全就好。
4、前端二维码框的html和css代码诸位不会找我要了吧,毕竟你们都是大牛嘛。
5、能看到这里,真的挺感谢,没白写一场,另外,大牛们打击的时候手下留情些,我还有第二版呢,也许比这个好哦。
以上是关于初涉扫码登录:edusoho实现客户端扫码登录(简版)的主要内容,如果未能解决你的问题,请参考以下文章