扫码登录的实现原理

原理解释:

接下来就是对于这个服务的详细实现。首先,大概说一下原理:用户打开网站的登录页面的时候,向浏览器的服务器发送获取登录二维码的请求。服务器收到请求后,随机生成一个uuid,将这个id作为key值存入redis服务器,同时设置一个过期时间,再过期后,用户登录二维码需要进行刷新重新获取。同时,将这个key值和本公司的验证字符串合在一起,通过二维码生成接口,生成一个二维码的图片(二维码生成,网上有很多现成的接口和源码,这里不再介绍。)然后,将二维码图片和uuid一起返回给用户浏览器。

浏览器拿到二维码和uuid后,会每隔一秒向浏览器发送一次,登录是否成功的请求。请求中携带有uuid作为当前页面的标识符。这里有的同学就会奇怪了,服务器只存了个uuid在redis中作为key值,怎么会有用户的id信息呢?

这里确实会有用户的id信息,这个id信息是由手机服务器存入redis中的。具体操作如下:

手机端+服务器

话说,浏览器拿到二维码后,将二维码展示到网页上,并给用户一个提示:请掏出您的手机,打开扫一扫进行登录。用户拿出手机扫描二维码,就可以得到一个验证信息和一个uuid(扫描二维码获取字符串的功能在网上同样有很多demo,这里就不详细介绍了)。由于手机端已经进行过了登录,在访问手机端的服务器的时候,参数中都回携带一个用户的token,手机端服务器可以从中解析到用户的userId(这里从token中取值而不是手机端直接传userid是为了安全,直接传userid可能会被截获和修改,token是加密的,被修改的风险会小很多)。手机端将解析到的数据和用户token一起作为参数,向服务器发送验证登录请求(这里的服务器是手机服务器,手机端的服务器跟网页端服务器不是同一台服务器)。服务器收到请求后,首先对比参数中的验证信息,确定是否为用户登录请求接口。如果是,返回一个确认信息给手机端。

手机端收到返回后,将登录确认框显示给用户(防止用户误操作,同时使登录更加人性化)。用户确认是进行的登录操作后,手机再次发送请求。服务器拿到uuId和userId后,将用户的userid作为value值存入redis中以uuid作为key的键值对中。

登录成功

然后,浏览器再次发送请求的时候,浏览器端的服务器就可以得到一个用户Id,并调用登录的方法,声成一个浏览器端的token,再浏览器再次发送请求的时候,将用户信息返回给浏览器,登录成功。这里存储用户id而不是直接存储用户信息是因为,手机端的用户信息,不一定是和浏览器端的用户信息完全一致。


手机端实现步骤

使用hbuilder X 开发工具,新建 5+App项目。

1、修改index.html页面,在页面添加扫一扫按钮

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /><title>首页</title><link href="css/mui.min.css" rel="stylesheet" /><style>.title{margin: 20px 15px 10px;color: #6d6d72;font-size: 15px;}.oa-contact-cell.mui-table .mui-table-cell {padding: 11px 0;vertical-align: middle;}.oa-contact-cell {position: relative;margin: -11px 0;}.oa-contact-avatar {width: 75px;}.oa-contact-avatar img {border-radius: 50%;}.oa-contact-content {width: 100%;}.oa-contact-name {margin-right: 20px;}.oa-contact-name, oa-contact-position {float: left;}h5{padding-top: 8px;padding-bottom: 8px;text-indent: 12px;}</style></head><body><header class="mui-bar mui-bar-nav" style="background-color: darkblue;"><h1 class="mui-title" style="color: #FFFFFF;font-size: 20px;">手机扫一扫</h1></header><div class="mui-content"><!-- 工作台 --><div id="tabbar" class="mui-control-content mui-active"><h5 style="background-color:#efeff4">工作台</h5><hr style="margin: 0;"><ul class="mui-table-view mui-grid-view mui-grid-9"><li class="mui-table-view-cell mui-media mui-col-xs-6 mui-col-sm-6" id="scanBtn"><a href="#"><span class="mui-icon"> <img src="data:images/icon_scan.png" width="100%" height="100%" /></span><div class="mui-media-body">扫码登录</div></a></li></ul>  </div></div><script src="js/mui.min.js"></script><script>(function($, doc) {$.init({swipeBack:true});var scanPage = null;  $.plusReady(function() {//判断手机是否开启gps// if (plus.os.name=="Android"){  //  var context = plus.android.importClass("android.content.Context");//     var locationManager = plus.android.importClass("android.location.LocationManager");//     var main = plus.android.runtimeMainActivity();//     var mainSvr = main.getSystemService(context.LOCATION_SERVICE);//     var androidIsOpen = mainSvr.isProviderEnabled(locationManager.GPS_PROVIDER);//     if(androidIsOpen == false){//        plus.nativeUI.alert("当前位置不可用,请手动开启GPS", function(){//          plus.runtime.quit();//      }, "系统提示", "确定");//         return;//     }// }//扫码登录var scanBtn = doc.getElementById('scanBtn');scanBtn.addEventListener('tap', function(event) {$.openWindow({id: 'scan',url: 'scan.html?v='+ new Date().getMilliseconds(),createNew: true,show: {aniShow: 'pop-in'},styles: {popGesture: 'hide'},waiting: {autoShow: true}});}); });}(mui, document));</script></body>
</html>

2、添加san.html页面,扫码页面

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /><title>扫码登录</title><link href="css/mui.min.css" rel="stylesheet" /><style type="text/css">#bcid {width: 100%;height: 100%;position: absolute;background: #000000;}html,body,div {height: 100%;width: 100%;}.fbt {color: #0E76E1;width: 100%;background-color: #ffffff;float: left;line-height: 44px;text-align: center;}</style></head><body><header class="mui-bar mui-bar-nav" style="background-color: #4682B4;"><a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left" style="color:#FFFFFF;"></a><h1 class="mui-title" style="color: #FFFFFF;font-size: 20px;">扫码登录</h1><span class="mui-icon mui-icon-spinner-cycle mui-spin mui-pull-right" id="turnTheLight"></span></header><div id="bcid"><!--盛放扫描控件的div--></div><div class="mui-bar mui-bar-footer" style="padding: 0px;"><!-- <div class="fbt" onclick="scanPicture();">从相册选择二维码</div>--><div class="fbt mui-action-back">取消扫码</div></div><script src="js/mui.min.js"></script><script>var scan = null; //扫描对象var scanResultPage = null; (function($, doc) {$.init({statusBarBackground: '#f7f7f7', preloadPages:[{id:'scanResult',  url:'scanResult.html'             }  ]  });$.plusReady(function() {startRecognize();});//处理扫码结果function processResult(resultUrl){//获得详情页面  if(!scanResultPage){  scanResultPage = plus.webview.getWebviewById('scanResult');  }  //触发详情页面的newsId事件  $.fire(scanResultPage,'scanResult',{  resultUrl: resultUrl});  //打开详情页面            mui.openWindow({  id:'scanResult'  });  }//初始化扫码控件function startRecognize() {try {var filter;//自定义的扫描控件样式var styles = {top: '100px',left: '0px',width: '100%',height: '500px',position: 'static',}//扫描控件构造scan = plus.barcode.create('bcid', filter, styles);scan.onmarked = onmarked;scan.onerror = onerror;plus.webview.currentWebview().append(scan);scan.start();//打开关闭闪光灯处理var flag = false;document.getElementById("turnTheLight").addEventListener('tap', function() {if (flag == false) {scan.setFlash(true);flag = true;} else {scan.setFlash(false);flag = false;}});} catch (e) {//alert("出现错误啦:\n" + e);plus.nativeUI.alert(e, "扫码出错", "确定");}};function onerror(e) {alert(e);};function onmarked(type, result) {var text = '';switch (type) {case plus.barcode.QR:text = 'QR: ';break;case plus.barcode.EAN13:text = 'EAN13: ';break;case plus.barcode.EAN8:text = 'EAN8: ';break;}//扫描成功之后的处理//假如不是网址 或者 是文本等其他内容直接提示if (result.indexOf('https://') != -1 || result.indexOf('http://') != -1){//扫码正确setTimeout(function(){scan.start();},1000);processResult(result);}else{             plus.nativeUI.alert(result, function(){scan.start();}, "扫码数据有误", "确定");}//处理之后再重新开始扫描//scan.start();};// 从相册中选择二维码图片 function scanPicture() {plus.gallery.pick(function(path) {plus.barcode.scan(path, onmarked, function(error) {//plus.nativeUI.alert("无法识别此图片");plus.nativeUI.alert("该图片无法识别,请更新", "扫码结果", "确定");});}, function(err) {//plus.nativeUI.alert("Failed: " + err.message);plus.nativeUI.alert(err.message, "扫码失败", "确定");});}}(mui, document));</script> </body>
</html>

3、手机端扫码二维码后,需要显示扫码结果页面。这里添加sanResult.html

<!DOCTYPE html>
<html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" /><title>扫码结果</title><link href="css/mui.min.css" rel="stylesheet" /><style type="text/css">#bcid {width: 100%;height: 100%;position: absolute;background: #000000;}html,body,div {height: 100%;width: 100%;}.fbt {color: #0E76E1;width: 100%;background-color: #ffffff;float: left;line-height: 44px;text-align: center;}</style> </head><body><header class="mui-bar mui-bar-nav" style="background-color: #4682B4;"><a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left" style="color:#FFFFFF;"></a><h1 class="mui-title" style="color: #FFFFFF;font-size: 20px;">扫码结果</h1></header><div class="mui-content" style="background-color:#fff"><div class="mui-content-padded" style="padding-top: 50px;"><div id="resultDiv" style="display: none;"><p style="word-break: break-all;">如需浏览,请长按网址复制后使用浏览器访问<br /><br /></p><p id="resultUrlDiv" class="copy-text" style="word-break: break-all;"></p></div><div id="loginDiv" style="display: none;"><div style="width: 100%;height: 30px;text-align: center;">XXXX系统网页登录确认</div><div style="width: 100%;text-align: center;"><img src="data:images/icon_pc.png" width="230" height="230" /><button id='loginBtn' type="button" class="mui-btn mui-btn-block" style="background-color: #4682B4;color: #FFFFFF;">确认登录</button><button id='cancelBtn' type="button" class="mui-btn mui-btn-block" >取消登录</button></div></div ></div></div><script src="js/mui.min.js"></script><script src="js/app.js"></script><script>var settings = {};(function($, doc) {$.init({statusBarBackground: '#f7f7f7',gestureConfig: {longtap: true, //默认为false}});$.plusReady(function() {var requestUrl = "";//获取用户信息// settings = app.getSettings();//监听自定义事件,用于接收传过来的值window.addEventListener('scanResult', function(event) {//通过event.detail可获得传递过来的参数内容var resultUrlDiv = document.getElementById("resultUrlDiv");var resultUrl = event.detail.resultUrl;if (resultUrl && resultUrl.length > 0){             resultUrlDiv.innerHTML = resultUrl ;requestUrl = event.detail.resultUrl;//验证扫描结果网址是否是网站地址plus.nativeUI.alert("请求地址: " + resultUrl );//  if (resultUrl.indexOf(ScanBasePath) != -1 || resultUrl.indexOf(ScanBaseIPPath) != -1){//      //更新扫描状态//      resultUrl += "&confirmFlag=0";//       resultUrl += "&loginId=" + settings.loginId;//        app.updateQRStatus(//           resultUrl,//            {},//           function(resultObject) {//  if (resultObject) {//       if (resultObject.code == 1000){//             document.getElementById("loginDiv").style.display = "block";//         }else if(resultObject.code == 1003){//            plus.nativeUI.alert("二维码已过期,需重新扫码", function(){//              $.back();//             }, "系统提示", "确定");//         }//         else if(resultObject.code == 1005){//             plus.nativeUI.alert(resultObject.msg, function(){//                 app.setSettings({});//              app.setLocalTrainItemList("");//              plus.webview.getLaunchWebview().show("pop-in");//             }, "系统提示", "确定");//         }//         else{//             plus.nativeUI.alert(resultObject.msg, function(){//                 $.back();//             }, "系统提示", "确定");//         }                                   //  }// }//         );//    }else{//        document.getElementById("resultDiv").style.display = "block";//    }}else{plus.nativeUI.alert("二维码数据有误,请稍后再试", function(){}, "系统提示", "确定");}});//确认操作var loginBtn = document.getElementById("loginBtn");loginBtn.addEventListener("tap" , function(event){if (requestUrl && requestUrl.length > 0){if (requestUrl.indexOf(ScanBasePath) != -1 || requestUrl.indexOf(ScanBaseIPPath) != -1){//更新扫描状态requestUrl += "&confirmFlag=1";requestUrl += "&loginId=" + settings.loginId;app.updateQRStatus(requestUrl,{},function(resultObject) {if (resultObject) {if (resultObject.code == 1000){plus.nativeUI.alert("确认成功", function(){document.getElementById("loginBtn").setAttribute("disabled" , "disabled");document.getElementById("loginBtn").value = "您已确认";document.getElementById("loginBtn").textContent = "您已确认";}, "系统提示", "确定");document.getElementById("resultUrlDiv").style.display = "inline-block";}else if(resultObject.code == 1005){plus.nativeUI.alert(resultObject.msg, function(){app.setSettings({});app.setLocalTrainItemList("");plus.webview.getLaunchWebview().show("pop-in");}, "系统提示", "确定");}else if(resultObject.code == 1003){plus.nativeUI.alert("二维码已过期,需重新扫码", function(){}, "系统提示", "确定");}else{plus.nativeUI.alert(resultObject.msg, function(){}, "系统提示", "确定");}                                    }});}}});//取消操作var cancelBtn = document.getElementById("cancelBtn");cancelBtn.addEventListener("tap" , function(){$.back();});// 使用mui的长按事件 我把我的复制元素 class 设置为copy-text$('body').on('longtap', '.copy-text', function () {// 每次触发事件就会使用 innerText 获取纯文本。var copy_content = this.innerText;// 加了一个确认框 让用户选择是否复制plus.nativeUI.confirm('您要复制内容吗?', function (e) {if (e.index == 1) {//判断是安卓还是iosif (mui.os.ios) {// ios 的方法 这个我没具体研究过 直接拿来用了var UIPasteboard = plus.ios.importClass("UIPasteboard");var generalPasteboard = UIPasteboard.generalPasteboard();//设置 复制的内容也就是 触发事件 innerText 获取的内容generalPasteboard.plusCallMethod({setValue: copy_content,forPasteboardType: "public.utf8-plain-text"});generalPasteboard.plusCallMethod({valueForPasteboardType: "public.utf8-plain-text"});// 在上边都走完 给用户一个提示plus.nativeUI.toast('复制成功');} else {//安卓 的方法 这个我没具体研究过 直接拿来用了var context = plus.android.importClass("android.content.Context");var main = plus.android.runtimeMainActivity();var clip = main.getSystemService(context.CLIPBOARD_SERVICE);plus.android.invoke(clip, "setText", copy_content);// 在上边都走完 给用户一个提示plus.nativeUI.toast('复制成功');}}}, "提示", ['取消', '复制内容'])})});}(mui, document));</script></body>
</html>

效果图片

手机能扫码并得到http地址,剩下的需要自己处理了。

手机端源码下载:

链接:https://pan.baidu.com/s/1HbvjVyO5Sj_UFxUxBQVXSg
提取码:ko6h


参考资料:

扫码登录的实现原理

MUI 结合 HTML5+ 实现的二维码扫描功能

实现手机扫描二维码进行登录

Java扫码登录原理

MUI 结合 HTML5+ 实现的二维码扫描功能相关推荐

  1. 基于MUI框架的使用HTML5+实现的二维码扫描功能

                                                                               Barcode的一个实现案例 一.简介 Barco ...

  2. iOS 自带二维码扫描功能的实现

    #自从iOS7以后中新增了二维码扫描功能.因此可以在不借助第三方类库的情况下简单的写出二维码的扫描功能: 原生的二维码扫描功能在AVFoundation框架下,所以在使用原生的二维码扫描功能时要先导入 ...

  3. Android实现二维码扫描功能(四)-ZXing识别图片二维码,相册选图

    简介 上一篇 Android实现二维码扫描功能(三)-闪光灯控制介绍了光线较弱情况下开启闪光灯来辅助二维码识别的方法. 本篇我们介绍如何识别相册中的图片(含二维码) 动态演示 使用模拟器录制了动画演示 ...

  4. JavaCV/OpenCV 二维码扫描功能

    JavaCV/OpenCV 二维码扫描功能 怎样配置工程就不再赘述,不清楚的读者可以网上查找资料,二维码扫描功能通过JavaCV实现起来还是挺简单的,主要OpenCV中QRCodeDetector提供 ...

  5. Android实现二维码扫描功能-ZXing识别图片二维码,相册选图

    文章目录 1.演示 2.权限问题 3.实现步骤 4.工具类 5.图片Uri处理(重要更新) 1.演示 2.权限问题 部分朋友在打开相册时遇到读写权限未授权的问题,我在开发的时候没有遇到,也没有注册读写 ...

  6. Flutter 3.X二维码扫描功能

    Flutter 3.X二维码扫描功能 1. pubspec.yaml文件添加依赖 2使用 3. 源代码 4.第二种方式 4.1 pubspec.yaml文件添加依赖 4.2 使用 源代码 1. pub ...

  7. 使用安卓实现一个二维码扫描功能(基于Android Studio)

    二维码扫描使用很广泛,在这里记录二维码扫描的功能,需要导入第三方的类库. 步骤一:在项目中导入第三方的类库,导入方法:往Android Studio中导入第三方类库文件这篇博客中有详细记录 步骤二:定 ...

  8. Android实现二维码扫描功能(一)ZXing插件接入

    简介 关于Android扫描二维码的功能实现,网上有很多相关资料.在对比之后,选用了前辈了修改过的ZXing直接接入到项目中,特制作此demo,介绍整个过程. (最新更新)本篇文章讲解的接入方法对部分 ...

  9. Android直播带货系统中如何实现二维码扫描功能

    自李佳琦的出现将直播带货成功的引向了"巅峰",为了响应众平台和用户的需求,开发Android直播带货系统成为当下的开发热潮.那么在平时的直播软件中,登录.分享.支付等场景中经常出现 ...

最新文章

  1. python爬虫进阶案例_Python爬虫进阶必备 | MD5 hash 案例解析讲解
  2. opencv图像灰化_opencv读入图像、灰度化、归一化、向量化
  3. WinForms项目升级.Net Core 3.0之后,没有WinForm设计器?
  4. mysql 拷贝安装_Mysql的安装和主从复制
  5. shell脚本打开一个新终端并运行指定脚本
  6. python中xpath定位_xpath最新:关于python中的xpath解析定位_爱安网 LoveAn.com
  7. JEESITE快速开发平台(二)环境搭建
  8. python入门爬虫案例_[Python入门学习]-爬虫项目案例讲解
  9. c语言题目tcl是什么意思,TCL语言简述
  10. 龙讯7号 国芯发布龙芯电脑引争议
  11. 洛谷blog传送门qwq
  12. Python 招聘信息爬取及可视化
  13. 华为服务器管理口在哪个位置,华为服务器默认管理口地址吗
  14. 万字长文看懂商业智能(BI)|推荐收藏
  15. JavaScript 面试题汇总
  16. 点阵屏HCMS-3977驱动
  17. Rails 内置方法大全(慢慢积累中)
  18. 【gflags 】google gflags 使用方法
  19. 把文字转换成拼音工具类
  20. 智慧水务平台、智慧水务监管平台——智慧水务整体解决方案

热门文章

  1. JESD204接口调试总结——Xilinx JESD204C IP AXI寄存器简介
  2. 2020最新阿里云计算ACP考试笔记
  3. 【已解决】Discuz论坛头像无法上传,出现access……
  4. JAVA语言课程设计——扫雷小游戏
  5. 【转】隐马尔科夫模型(HMM)及其Python实现
  6. linux重启docker服务,如何解决Centos下Docker服务启动无响应,且输入docker命令无响应?...
  7. 图像的变换——fft/ ifft、fftn、fft2、dct2、dict2、dctmtx
  8. Matlab入门基础
  9. jpg和png的区别,应用场合有哪些?
  10. 游戏服务器要什么硬盘,云服务器用什么硬盘