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

记录一下最近在做的一个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) < 300) { // 且 没有超时
                    switch ($arr['type']) {
                        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具体逻辑思维图:

在这里插入图片描述

发布了16 篇原创文章 · 获赞 13 · 访问量 2461

猜你喜欢

转载自blog.csdn.net/weixin_42905245/article/details/102457843