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

实战剖析: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具体逻辑思维图:

你可能也喜欢

国际足联宣布新版世俱杯创纪录奖金方案
beat365中文官网

国际足联宣布新版世俱杯创纪录奖金方案

📅 07-20 👀 3683
ps软件哪个好?十年老用户推荐这两个给你
beat365中文官网

ps软件哪个好?十年老用户推荐这两个给你

📅 07-31 👀 978
手机记事本软件排行榜
beat365中文官网

手机记事本软件排行榜

📅 07-15 👀 5007
劲舞团戒指图鉴及升级方法(戒指效果图与升级奖励详解)
暗黑3彩虹翅膀怎么刷
365bet官网地址

暗黑3彩虹翅膀怎么刷

📅 06-30 👀 2438
大天使之剑手游如何转巅峰 大天使之剑巅峰升级之路攻略