初涉扫码登录: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);//
$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实现客户端扫码登录(简版)的更多相关文章
- Java实现微信客户端扫码登录
此篇文章记录自己开发中的微信客户端扫码登录的实例以及步骤,便于以后自行学习记起的关键,看到的网友有借鉴的地方就借鉴,看不懂的也请别吐槽,毕竟每个人的思维和思路以及记录东西的方式不一样: 1.首先需要一 ...
- 扫码下单与ERP客户端锁桌功能FAQ
一.需求场景:因为目前客户端和平台端有两套数据库,两套数据库通过网络交互信息,且双方都可以发起支付,这种结构容易造成: 1.一笔订单同时支付.一笔订单支付时未按最新订单进行支付,支付多付.支付少付的情 ...
- CAS单点登录源码解析之【客户端】
cas 3.5.3服务器搭建+spring boot集成+shiro模拟登录(不修改现有shiro认证架构).因为我们属于供应商,所以有些客户那里会需要接对方的CAS,所以没有使用shiro和cas的 ...
- 开启网易邮箱客户端授权码-POP/SMTP/IMAP
打开网易邮箱首页 https://mail.163.com/ 登录邮箱. 点击上方设置,选择POP/SMTP/IMAP选项. 选择开启对应的协议,IMAP或者POP3分别为不同的收信协议 在新弹出的弹 ...
- 单点登录 - OAuth 2.0 授权码模式(一)
OAuth 2.0定义了四种授权方式 授权码模式(authorization code) 简化模式(implicit) 密码模式(resource owner password credentials ...
- 单点登录CAS系列第06节之客户端配置单点登录
原理 纯web.xml 借助Spring 注意 代码 测试 原理 这里用的是:cas-client-core-3.4.0.jar(2015-07-21发布的) 下载地址为:http://mvnrepo ...
- ASP.NET Core3.1使用IdentityServer4中间件系列随笔(五):创建使用[Code-授权码]授权模式的客户端
配套源码:https://gitee.com/jardeng/IdentitySolution 本篇将创建使用[Code-授权码]授权模式的客户端,来对受保护的API资源进行访问. 1.接上一篇项目, ...
- swift实现饭否应用客户端源码
swift 版 iOS 饭否客户端 源码下载:http://code.662p.com/view/13318.html 饭否是中国大陆地区第一家提供微博服务的网站,被称为中国版Twitter.用户可通 ...
- android版高仿淘宝客户端源码V2.3
android版高仿淘宝客户端源码V2.3,这个版本我已经更新到2.3了,源码也上传到源码天堂那里了,大家可以看一下吧,该应用实现了我们常用的购物功能了,也就是在手机上进行网购的流程的,如查看产品(浏 ...
随机推荐
- phpMyAdmin访问远程MySQL数据库的方法
本地phpmyadmin远程连接服务器端MySQL 首先要确定你的mysql远程连接已开启,如果没有开启按照下面的二个方法操作: 方法一:改表法 因为在linux环境下,默认是关闭3306端口远程连接 ...
- tp系统常量定义
(2013-03-06 14:16:31) 转载▼ 标签: it 是已经封装好的系统常量 主要是用在控制器下面的动作当中 这样能很大的提高我们的开发效率 主要有下面的一些 手册上面都有的 ...
- PHP批量去除bom头代码的小工具
在 aitecms 群里有网友抱怨了好几天说本地的验证码一直无法显示,后来听说解决了,问其如何解决的,说是去除了文件 bom 就好了.后来百度到一篇文章也说 dedecms 的验证码不能显示,某次解决 ...
- 数据库复习总结(17)-T-Sql编程
T-SQL(SQL SERVER) 百度百科:(即 Transact-SQL,是 SQL 在 Microsoft SQL Server 上的增强版,它是用来让应用程序与 SQL Server 沟通的主 ...
- 谁能教我iCloud怎么用?
iCloud是苹果公司所提供的云端服务,使用者可以免费储存5GB的资料.你已经开始使用IOS5,并且你很兴奋的着手于将它同步至云服务层.以下就是怎样让你的设备更新至云服务层的非常简单的步骤.在你的iO ...
- JAVA 一句话技巧
1.拆分字符串 遇到特殊字符,比如:对‘$’符号,就应该使用‘\\$’,后总结可以加个方括号如 "[.]".2.遍历HASHMAP Iterator itr = map.keySe ...
- oracle02
SQL语句完整结构: select from where group by having order by 今天分享的知识点:(1)分组查询 select 中非组函数的列需要在group by 进行参 ...
- linux_运维职责
运维准则: 不要删文件,移动文件,可以复原,一个月后什么事没有,删除 运维人员主要关注哪些方面? CPU:对计算机工作速度和效率起决定性作用(intel amd) 内存: 临时存放数据:容量和处理速度 ...
- Android之MaterialDesign应用技术2-仿支付宝上滑搜索框缓慢消失
PS:在这之前也就是上一篇介绍了MaterialDesign一些滑动删除.标题栏的悬浮效果等,如果没看过第一篇的小火鸡可以看一下,因为这篇是接着上一篇写的,有一些东西上一篇写过了这里就不在重复了(Ma ...
- 《共享库PATH与ld.so.conf简析》
这是摘抄<共享库PATH与ld.so.conf简析>1. 往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf的,但是完了之后要调一下ldconfig,不然这个 ...