1、引言
扫码登录这个功能,最早应该是微信的PC端开始搞,不得不说还是很神奇的。
本文将简要的介绍扫码登录功能的技术实现逻辑
一、基本技术原理
a. 扫码登录功能到底是什么样的?
首先介绍下什么是扫码登录。现在大部分同学手机上都装有微信、qq和淘宝这一类的软件。而这些app都有他们相对应的网页端。为了让用户在使用他们的网页时登录更加方便和安全,使用手机扫一扫就可以登录的服务,就显得自然而然了
几个主流大厂应用扫码登录时的界面效果如下:
有很多小伙伴可能会感到很神奇,网页上只是显示了个二维码,它怎么就知道是哪个手机扫到了二维码,并且进行登录的呢?而且,登录完成以后,还能直接把用户信息显示给用户,真的是很神奇啊,接下来我们看原理
b. 扫码登录功能的完整技术逻辑
(1)网页端
首先用户打开网站的登录页面选择扫码登录的时候,会向服务器发送获取登录二维码的请求。服务器收到请求后,随机生成一个uuid,将这个id作为key值存入redis服务器,同时设置一个过期时间和扫码状态,再过期后,用户登录二维码需要进行刷新重新获取。
更多是时候,这个key值和公司的验证字符串合在一起,通过二维码生成接口,生成一个二维码的图片,然后将二维码图片和uuid一起返回给浏览器,或者将UUID和过期时间返回给浏览器,前端生成二维码。
浏览器生成二维码后,会每隔一秒向服务器发送一次登录是否成功的请求,来判断用户当前是否已完成扫码或者是否已完成登录还是二维码已过期。请求中携带有uuid作为当前标识符。
(2)移动端
浏览器拿到二维码后,将二维码展示到网页上,并给用户一个提示:请扫描二维码登录;
用户拿出手机扫描二维码,就可以得到一个验证信息和一个uuid
由于手机端已经进行过了登录,扫描解析出二维码时会向服务器发送一个请求,参数中都会携带一个用户的token,服务器可以从中解析到用户的userId(这里从token中取值而不是手机端直接传userid是为了安全,直接传userid可能会被截获和修改,token是加密的,被修改的风险会小很多,后面的dome我是为了方便)
扫码完成首先向服务器发送扫码完成的请求
服务器收到请求后,首先对比参数中的验证信息,确定是否为用户登录请求接口。如果是,首先修改redis服务器中此UUID的扫码状态为扫码中,并且浏览器拿到数据后提示用户点击登录,然后返回一个确认信息给手机端,
手机端收到返回后,将登录确认框显示给用户(不经过点击登录这一步直接登录比较反人类,交互性比较差,需要扫码直接登录的话,只需要删除这一步就好)。用户确认是进行的登录操作后,手机再次发送请求。服务器拿到uuId和userId后,将用户的userid作为value值存入redis中以uuid作为key的键值对中。
(3)登录成功
然后,浏览器再持续发送请求中,浏览器端就可以得到一个用户Id,并调用登录的方法,生成一个浏览器端的token,再浏览器再次发送请求的时候,将用户信息返回给浏览器,登录成功。这里存储用户id而不是直接存储用户信息是因为,手机端的用户信息,不一定是和浏览器端的用户信息完全一致。
如图是来自IM即时通讯社区的一张图
二,简单dome代码解析
首先完成这个功能需要三部分
1.网页端,主要显示二维码,使用到了jQuery(二维码生成插件)
2.后端,PHP,这里没有准备redis服务器,直接使用数据库
3.移动端,用来扫码,使用uniapp开发
首先后端,简单dome没什么多的功能,只是数据库读写
首先需要两张表
1.tap表,记录请求数据,包括四个字段,唯一标识UUID,过期时间time,扫码状态isok,扫码账号user
2.user表,模拟用户数据,验证登录
需要几个接口
1.二维码请求接口,生成唯一标识UUID和过期时间,写入数据库并返回此UUID和过期time,唯一UUID生成方法比较多,我这里直接使用时间戳做唯一UUID
$time = time()+300;//时间
$uuid = time();//唯一id的的生成方法太多,这里直接使用时间戳
$sql= "INSERT INTO `tap`(uuid,time,isok,user) VALUES ('$uuid','$time','false','none')";
$obj=mysqli_query($link,$sql);
//写入tap表
if($obj){
//输出
$results = array(
'uuid' => $uuid,
'time' => $time
);
$result = array(
'code' => 0,
'data' => $results
);
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}else{
echo '出现错误';
}
2.修改扫码状态为扫码中的接口,此处将扫码状态修改为‘ing’
$sql = "UPDATE tap SET isok='ing' WHERE uuid='$uid'";
//写入对应账号修改扫码状态为扫码中
$obj = mysqli_query($link, $sql);
if ($obj) {
echo "true";
} else {
echo "false";
}
3.写入对应UUID扫码状态为扫码完成和记录扫码账户接口
$sql = "UPDATE tap SET isok='true',user='$user' WHERE uuid='$uid'";
//写入对应账号修改扫码状态
$obj = mysqli_query($link, $sql);
if ($obj) {
echo "true";
} else {
echo "false";
}
4.来查询唯一UUID下所记录数据的接口
header('content-type:application/json;charset=utf8');
$sql ="SELECT * FROM tap where uuid='$uid'";
$obj = mysqli_query($link,$sql);
$total_sql = "SELECT COUNT(*) FROM tap";
$total_result = mysqli_fetch_assoc(mysqli_query($link, $total_sql));
$total = $total_result['COUNT(*)'];
$results = array();
while ($row = mysqli_fetch_assoc($obj)) {
$results[] = $row;
}
$result = array(
'code' => 0,
'message' => "",
'count' => $total,
'data' => $results
);
if($result){
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}else{
echo mysql_error();
}
5.查询用户信息的接口
header('content-type:application/json;charset=utf8');
$sql ="SELECT * FROM users where text='$user'";
$obj = mysqli_query($link,$sql);
$total_sql = "SELECT COUNT(*) FROM users";
$total_result = mysqli_fetch_assoc(mysqli_query($link, $total_sql));
$total = $total_result['COUNT(*)'];
$results = array();
while ($row = mysqli_fetch_assoc($obj)) {
$results[] = $row;
}
$result = array(
'code' => 0,
'message' => "",
'count' => $total,
'data' => $results
);
if($result){
echo json_encode($result,JSON_UNESCAPED_UNICODE);
}else{
echo mysql_error();
}
最后为了测试方便,写一个添加用户的接口
$sql= "INSERT INTO `users`(text,pass,name) VALUES ('$addText','$addPass','$addName')";
$obj=mysqli_query($link,$sql);
if($obj){
echo '添加成功';
}else{
echo '出现错误';
}
这些接口已tap做标识来分别调用
有这些接口后就可以开始完成具体功能了,接下里分两部分,分别是移动端和网页
首先,用户在登录时选择扫码登录功能,此时请求后端生成唯一UUID和过期时间,拿到数据后生成二维码显示
首先写好HTML结构,因为扫码流程中有显示二维码,扫码完成,扫码中,已过期多种情况
<div id="code"></div>
<div class=" tw star">
<p>加载中...</p>
</div>
<div class="tw cok">
<p>扫码成功</p>
</div>
<div class="tw ok">
<p>登录成功</p>
</div>
<div class="tw bard">
<p>出现错误</p>
</div>
<div class="tw time">
<p>扫码超时</p>
</div>
开始请求后端,调用接口并获取数据
请求失败则直接显示出现错误
请求成功则将拿到的UUID生成二维码并显示出来
然后创建一个计时器(int),在设定的过期时间之前轮询查询UUID数据接口,来根据所携带的isok(扫码状态)来判断当前是否扫码中,每次轮询都都需要判断是否过期,如果没有过期继续轮询,直到扫码状态变成扫码中
此时已经判断在扫码中,现在立即关闭第一个计时器(int),修改页面提示为扫码中
然后再次创建一个计时器(int2),在计时器中轮询UUID对应数据接口,每次轮询依然判断是否过期,在没有过期的前提下继续轮询判断是否扫码完成
在轮询过程中持续判断扫码状态是否为扫码完成,当条件成立时,立即关闭第二个计时器(int2),然后显示已成功登陆
接下来请求用户数据接口返回用户数据并显示
获取二维码
扫码中
扫码完成
接下来就是移动端
当页面出现二维码时,点击按钮开始扫码
扫码完成拿到二维码内容时,请求修改扫码状态为扫码中接口,
具体直接看图
当接口全部返回正常数据后,跳转到确认登录界面
用户点击确认按钮后,请求修改扫码状态为扫码完成,并且提示
至此,流程结束
完整dome
后端搭建
1.首先需要数据库建两张表,tap表和user表
tap表
user表
2.后端配置数据库
-打开config目录下的mysql.ini文件
如图填写数据库信息
3.配置页端
搭建后后端后,打开index.html文件,找到147行,修改后端api域名,结尾需要带/例如http://baidu,com/(请将api.php放到根目录)
4.配置移动端
搭建好后端后,打开uniapp项目,在pages目录下打开index/index.vue文件,修改第23行apiurl,内容和页端影一样
打开pages下的uigo/uigo.vue文件,修改15行
完整包下载地址见文章结尾
这只是一个dome,在实际开发中要比此复杂,但是原理相通,在实际开发中,服务端在修改扫码状态时需要核验用户账号密码,并且上传的用账号密码需要加密处理,切记
dome下载
扫码登录dome-补config.zip