赞
踩
1. 前言
首先说说为啥会接触软件的编码与开发呢?是因为这学期的“软件工程”必修课程要求每个小组开发一款软件。而我们打算开发的是一款课程签到软件,包括登录、注册、签到、查询、增删改等功能。在技术方面涉及web开发、小程序开发和服务器数据库的搭建。web开发我们主要使用php,小程序开发使用微信开发者工具
而我负责的是发起签到与扫码签到部分。即用户在web端点击发起签到后,web浏览器显示二维码并动态刷新,同时移动端微信小程序用户可以点击扫码功能进行扫码签到,如果扫码成功了则会在服务器数据库的签到记录表插入一条学生签到信息并在微信小程序弹窗提示成功信息。
为什么要在web浏览器上动态刷新二维码呢?其实是为了一定意义上防止诸如“某同学在教室拍二维码发给在宿舍睡觉的舍友,即使不去上课也能躲过课堂考勤”的现象~
2. 附带有效期的二维码
为什么拍照发回给舍友的二维码就失效了,原理是什么?主要的实现思路是在生成的二维码中加入了额外的计数信息,当二维码不断刷新的时候,这个计数就不断递增,当你拍照发给舍友之后,照片里的二维码计数信息相比当前计数信息已经滞后了很多,导致二维码失效而签到失败。
利用phpqrcode库生成二维码(1.php):
include "./phpqrcode/phpqrcode.php";
$numdata = intval($checking_num);
$qrdata = intval($checking_id + $numdata*10000); # 合并成一个数据,解码即可获取计数和checking id
$level = "L"; # 纠错级别,最多可识别已损失的数据, L--7%,M--15%,Q--25%,H--30%
$size = 15; # 二维码尺寸
$margin = 2; # 二维码边缘填充
QRcode::png($qrdata,false,$level,$size,$margin,false);
其中,checking_id是课程签到表的主键,代表某课程某次发起签到的id号,由checking_id可知对应课程在对应时刻的课堂签到总体情况。
二维码信息在函数的第一个参数(qrdata是随便命名的),其中包含了两个信息,即计数num 和 checking_id,对它进行 num × 10000 + checking_id 的编码操作将两个变量合成一个数据插入二维码中,简单解码即可得到计数num和checking_id,如 120010:
120010 % 10000 = 10(checking_id)
(120010 - checking_id)/ 10000 = 12(num)
二维码动态刷新(2.php):
<p>
<script>
function myrefresh()
{
//window.location.reload(); //只有这句就够了,下一行意思也是定位当前页面
window.location.replace("teacher_make_checking_go.php?class_id="+<?php echo $class_id;?>+"&checking_id="+<?php echo $checking_id;?>);
}
setTimeout('myrefresh()',1000); //指定1秒刷新一次
</script>
//展示二维码,并传参数,传入的php可用 GET获取checking_id
<img src="teacher_make_checking.php?<?php echo "checking_id=" . $checking_id;?>">
</p>
3. web端和小程序变量共享
上面基本上就将生成并动态刷新附带有效期的二维码的原理讲完了。二维码的有效期只与num和num’有关,当小程序上传扫码获取的计数num’到服务器后,服务器需要将num’和num进行比对,如果num - num’ < 一定范围(几秒,比拍照回传时间短即可),则说明签到成功。
一开始我想到的方法是使用SESSION全局变量在不同php之间传递计数num,以此可以在 3.php(1.php、2.php…代指不同页) 里对比小程序上传的num’和num的差值,并判断是否签到成功。然而我忽略了一个问题:服务器与客户端初次创建session时,在返回给客户端的header中有SetCookie,客户端会把里面的sessionid存到cookie中,这样下次请求服务器,服务器就能根据请求头中cookie信息确定客户端想要的SESSION,然而小程序请求服务器开启SESSION则又是另一个sessionid,不同的SESSION之间变量是不共享的,所以小程序请求服务器的php中根本无法得到web客户端当前的num,二者无法进行比较,如下图所示:
考虑到上述方法中不断递增的计数num是无法共享的,导致无法计算 num - num’的问题,我们则需要考虑一种能将计数num变量储存在服务器的方法,这样不同客户端便都可以访问了。
于是我决定web端在刷新二维码递增num的同时,根据checking_id主键将num储存进数据库中,这样在移动端扫码上传checking_id和num’到服务器时,服务器就能根据checking_id读取当前的计数num,再计算 num - num’ 判断是否签到成功或超时即可。
访问逻辑如下图所示:
数据库的设计如下:
获取数据库存储的计数num以及小程序扫码上传的num’并进行判定的php代码如下,如果签到成功就会将学生签到信息存进签到信息表里,并设置status发送给小程序用来在小程序中弹窗提示签到结果:
if ($conn->connect_error) { die("Could not connect to database!");} //获取小程序传递数据 $qrdata = intval(isset($_GET['qrdata_send']) ? $_GET['qrdata_send'] : 0); $student_id = intval(isset($_GET['student_id']) ? $_GET['student_id'] : 0); $checking_id = $qrdata % 10000; $num_get = ($qrdata - $checking_id) / 10000; $status = '0'; // 签到成功标志, 1成功,0失败 include "scan_mysql.php"; foreach ($sql_2_presented_result as $row){ $checking_num = $row['checking_num']; } // 保证GET到了数据 if ($qrdata!=0 && $student_id!=0) { if ($checking_num-$num_get<4) { $sql = "INSERT INTO signin_info(checking_id, student_id) value('$checking_id', '$student_id')"; $status = '1'; if (!mysqli_query($conn, $sql)){ die('Error: ' . mysqli_error($con)); $status = '0'; } } } // 关闭连接 $conn->close(); $res['status'] = $status; header("Content-type:application/json"); echo json_encode($res); die();
至此就成功实现了动态刷新二维码并给每一帧二维码附带有效期的功能,一定意义上解决了点名费时费力和普通扫码考勤容易伪造签到的问题。使大家不能好好在宿舍睡觉了~
如果有更好的解决方法,也欢迎指教~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。