实战剖析:app扫码登陆实现原理(app+网页端详细逻辑)附源码 您所在的位置:网站首页 使用手机扫二维码安全吗 实战剖析:app扫码登陆实现原理(app+网页端详细逻辑)附源码

实战剖析:app扫码登陆实现原理(app+网页端详细逻辑)附源码

#实战剖析:app扫码登陆实现原理(app+网页端详细逻辑)附源码| 来源: 网络整理| 查看: 265

记录一下最近在做的一个app扫码登陆的功能。

文章最底下附app以及网页端具体逻辑思维图

具体思路如下:

1.后台生成一个唯一值,附加到二维码上,返回给前端页面,这个唯一值保存到数据库里一份,用来后续的比对。(生成二维码的方法有很多种,网上很多这里就不过多的介绍了,后边有代码)。 2.前端AJAX轮询请求二维码的状态,判断是否已扫、确认登陆、取消登陆、超时等信息。 3.APP扫码,用户使用APP扫码后向网页端接口传递一个状态字段表示已扫,前端ajax轮询判断状态是已扫就隐藏掉二维码。用户点击确认登陆向网页端接口传递确认登陆状态,以及用户的唯一标识,前端ajax判断是确认登陆,获取到用户唯一标识后查询数据库存储对应session,跳转到对应页面。

/** * 生成二维码 * @param string $url 二维码中的内容,加http://这样扫码可以直接跳转url * @param string $message 二维码下方注释 * @param string $logo 二维码中间logo图片 * @param int $logo_w 图片大小 * @param int $size 二维码大小 * @return string 二维码 */ function qrcode($url, $message = '', $logo = '', $logo_w = 50, $size = 300) { $errorCorrectionLevel = 'L'; //容错级别 $matrixPointSize = 3; //生成图片大小 //生成二维码图片 QrCode::png($url, '../qr/qrcode.png', $errorCorrectionLevel, $matrixPointSize, 2); $logo = 'static/img/logo.png'; //准备好的logo图片 $QR = '../qr/qrcode.png'; //已经生成的原始二维码图 if ($logo !== FALSE) { $QR = imagecreatefromstring(file_get_contents($QR)); $logo = imagecreatefromstring(file_get_contents($logo)); if (imageistruecolor($logo)) imagetruecolortopalette($logo, false, 65535); $QR_width = imagesx($QR); //二维码图片宽度 $QR_height = imagesy($QR); //二维码图片高度 $logo_width = imagesx($logo); //logo图片宽度 $logo_height = imagesy($logo); //logo图片高度 $logo_qr_width = $QR_width / 6; $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); } //输出图片 imagepng($QR, '../qr/appdownload.png'); //base64二维码 $qrcode = file_get_contents('../qr/appdownload.png'); $qr_img = "data:image/jpg;base64," . base64_encode($qrcode); return $qr_img; }

生成二维码接口

/** * Notes: 生成二维码方法 */ public function sweepCodeOp() { if (request()->isGet() && request()->isAjax()) { // 创建token $token = get_token(); $check_token = check_token(); // 生成二维码 // 这里边的地址暂时是模拟地址 $qr = qrcode('ceshi.cn/user/login?token=' . $token . '&check_token=' . $check_token); $data = [ 'token' => $token, 'addtime' => time(), 'check_token' => $check_token, ]; // 新增二维码表 $res = model('qrcode')->allowField(true)->validate('qrcode.add')->save($data); if ($res === false) { return false; } return return_msg(1, '生成验证码成功', $qr, $token); } return $this->fetch(); }

前端点击获取二维码请求后台这个生成二维码的接口,判断code等于1标识获取二维码成功,然后开始定时轮询二维码状态的接口,具体ajax轮询如下:

var flag = true; var timer; var a = 1; function qrcode() { if (flag == true) { // 防止用户频繁点击 flag = false; clearInterval(timer); $.ajax({ type:"GET", url:"sweepCode", data:{}, success:function (adata) { var data = JSON.parse(adata); // console.log(data.token); // var token = data.token; if (data.code == 1) { // 1 表示二维码生成成功 $("#qr").attr('src',data.data); timer = setInterval(function () { // console.log(a); $.ajax({ type:"POST", url:"getStatus", data:{token:data.token}, success:function (res) { var ares = JSON.parse(res); // console.log('T--'+data.token); console.log(ares); switch (ares.code) { case 1201: // 1201 表示二维码过期 console.log(ares.msg); $("#qr").attr('src',''); clearInterval(timer); break; case 1205: // 表示用户扫描了二维码 $("#qr").attr('src',''); $("#success").css('display','block'); break; case 1207: // 1207 表示用户扫描过但点击取消登录 $("#success").css('display','none'); $("#rem").css('display','block'); clearInterval(timer); break; case 1202: // 1202 表示账号不存在 break; case 1203 : // 1203 表示账号未绑定成功 break; case 200: // 200 表示登录成功 里边要写跳转 $("#success").text(ares.username); clearInterval(timer); alert(ares.msg); // location.href="/index/index/index"; break; case 400: // 表示参数错误 clearInterval(timer); break; case 1211: // 数据异常 break; } } }); },2000); } else { console.log('未知错误!'); } } }); setTimeout(function () { // 设置点击频率 flag = true; },2000); a++; } else { console.log('点击过于频繁'); a -- ; } }

查询二维码状态的接口如下: (因为是实战项目用到的功能,所以判断以及遇到的各种情况的判断比较复杂,如果自己练习使用可以简化着写)

/** * Notes: 轮询查询二维码状态 * @return string */ public function getStatusOp() { if (request()->isAjax() && request()->isPost()) { $token = preg_replace('/\s/', '', input('token')); // 实例化二维码表 $qrcode = model('qrcode'); // 删除一些未轮询过期的二维码 $del = $qrcode->field('addtime,numid')->select(); foreach ($del as $v) { if (time() - $v['addtime'] > 400) { $qrcode->where(['numid' => $v['numid']])->delete(); } } // 实例化用户表 $user = model('user'); // 查二维码表 $result = $qrcode->where(['token' => $token])->find(); if (!empty($result)) { if (time() - $result['addtime'] > 300) { // 请求超时 $qrcode->where(['token' => $token])->delete(); return return_msg(1201, '二维码过期请刷新'); } switch ($result['status']) { case 0: // 表示未扫描 return return_msg(1200, '二维码未扫描,请扫描二维码'); break; case 1: // 表示已扫描 if ($result['qrstatus'] == 7) { // 二维码状态 7 为取消登录 $qrcode->where(['token' => $token])->delete(); // 删除二维码 return return_msg(1207, '二维码已取消授权'); } elseif ($result['qrstatus'] == 9) { // 二维码状态 9 为确认登录 if (!empty($result['uid'])) { // 查用户表 $res_user = $user->where(['numid' => $result['uid']])->field('username,numid')->find(); if ($res_user !== false) { // 给session赋值 session('username', $res_user['username']); session('uid', $res_user['numid']); // 删除二维码 $qrcode->where(['token' => $token])->delete(); return return_msg(200, '登录成功', session('username')); // 登录成功要跳转 } else { return return_msg(1202, '账号不存在'); } } else { // 删除二维码 $qrcode->where(['token' => $token])->delete(); return return_msg(1203, '账号未绑定'); } } else { return return_msg(1205, '请手机客户端确认登录'); } break; default: return return_msg(1211, '数据异常!'); break; } } else { return return_msg(400, '参数错误!'); } } }

APP传递参数的接口如下:

/** * Notes: app 传递过来参数 * @return string */ public function getAppOp() { if (request()->isPost()) { $arr = [ 'token' => preg_replace('/\s/', '', input('token')), 'check_token' => preg_replace('/\s/', '', input('check_token')), 'type' => intval(input('type')), // 1 扫过码 7 取消登录 9 确认登录 'uid' => intval(input('uid')), ]; // 实例二维码表 $qrcode = model('qrcode'); $token = $arr['token']; $check_token = $arr['check_token']; // 判断传递过来的token是否正确 $addtime = $qrcode->where(['token' => $token, 'check_token' => $check_token])->value('addtime'); if (!empty($addtime)) { // token正确 if ((time() - $addtime) case 1: // 表示已扫 $qrcode->isUpdate(true, ['token' => $token, 'check_token' => $check_token])->save(['status' => 1]); return return_msg(1300, '扫码成功!'); break; case 7: // 更新qrstatus 表示取消登录 $qrcode->isUpdate(true, ['token' => $token, 'check_token' => $check_token])->save(['qrstatus' => 7]); return return_msg(1301, '取消登录!'); break; case 9: // 表示确认登录 if (!empty($arr['uid'])) { $qrcode->isUpdate(true, ['token' => $token, 'check_token' => $check_token])->save(['qrstatus' => 9, 'uid' => $arr['uid']]); return return_msg(1302, '登录成功!'); } else { return return_msg(1303, '账号绑定失败!'); } break; default: return return_msg(1401, '数据异常!'); break; } } else { return return_msg(1402, '超时!'); } } else { return return_msg(1403, '验证失败!'); } } }

网页端具体逻辑思维图:

网页端具体逻辑

APP具体逻辑思维图:

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有