当前位置: 首页 > 编程日记 > 正文

微信扫描二维码登入实现,网页端

2019独角兽企业重金招聘Python工程师标准>>> hot3.png


服务器端要做得事很多,虽然逻辑不是很复杂,但是我们必须要分析清楚我们要做哪些事,请看下图:

通过这张图,我们看出,服务器端的接口一共有6个,分别处理:

  1. 手机客户端登录
  2. 首页
  3. 二维码图片流
  4. long polling维持
  5. 接收手机客户端已扫描的通知
  6. 接收手机客户端已确认登录的通知
那么一个一个解决

首先是手机客户端登录,在上一篇我们介绍的手机客户端登录我们仅仅模拟一下,因此用户只需要提交一个用户名,服务器则通过SHA1对用户名加密,将密文返回作为token。为了将来验证这个密文是否OK,我们将用户名和密文保存在redis内供将来验证使。
需要引用的包:
[javascript]  view plain copy
  1. var http = require('http'), url = require('url'), fs = require('fs'), querystring = require('querystring'),qrcode = require('qrcode'), UUID = require('uuid-js'), sha1 = require('sha1'), redis = require('redis'), redisClient = redis  
  2.         .createClient('10087''192.168.111.122'), redisKey = 'QRCODE_LOGIN_TOKEN';  

redis 的客户端也一并创建了,并设置了key
web服务的基础结构如下:
[javascript]  view plain copy
  1. http.createServer(function(req, res) {  
  2.     // parse URL  
  3.     var url_parts = url.parse(req.url);  
  4.     var path = url_parts.pathname;  
  5.     var uuid4 = UUID.create();  
  6.     var _sessionID = uuid4.toString();  
  7.     if (path == '/') {  
  8.         //...  
  9.     } else if (path == '/poll') {  
  10.         // console.log('polling');  
  11.           
  12.     } else if (path == '/qrcodeimage') {  
  13.         // 二维码的请求,参数为sessionID  
  14.   
  15.     } else if (path == '/moblogin') {  
  16.         // 返回用户名对应的token,简单采用sha1加密  
  17.           
  18.     } else if (path == '/scanned') {  
  19.         console.log('scanned');  
  20.           
  21.     } else if (path == '/confirmed') {  
  22.         console.log('confirmed');  
  23.           
  24.     } else {  
  25.         res.writeHead(200, {  
  26.             'Content-Type' : 'text/html; charset=UTF-8'  
  27.         });  
  28.         res.end();  
  29.     }  
  30. }).listen(9999, '192.168.111.109');  
  31. console.log('服务器已运行在端口9999.');  

通过分析,我们无非就是为这6个分支添加逻辑。
这次案例是一个试验,因此我们代码编写的也比较简单,如果使用类似express等框架的话,会更加方便一些。

先看看第一个接口,登录,返回sha1的token
[javascript]  view plain copy
  1. if (path == '/moblogin') {  
  2. <span style="white-space:pre">      </span>// 返回用户名对应的token,简单采用sha1加密  
  3.         var userName = urlDecode(url_parts.query);  
  4.         var token = sha1(userName);  
  5.         // userHash.set(token, userName);  
  6.         // 保存token到redis  
  7.         redisClient.hset(redisKey, token, userName);  
  8.         res.writeHead(200, {  
  9.             'Content-Type' : 'text/html; charset=UTF-8'  
  10.         });  
  11.         res.end(token);  
  12. <span style="white-space:pre">  </span>}  

下面是首页,如果用户敲击的url是一个不带参数的地址,事实上,用户初次访问肯定不带任何参数,而我们这个页面的目的是必须要有sessionID,因为首页内包含的2个子请求是必须具备sessionID参数的。因此我们要做url做一个分析和强制跳转:
[javascript]  view plain copy
  1. if (path == '/') {  
  2.         var sessionID = url_parts.query;  
  3.         if (typeof (sessionID) == "undefined" || sessionID == "") {  
  4.             // 访问首页没有参数,自动跳转  
  5.             res.writeHead(200, {  
  6.                 'Refresh' : '0; url=/?' + _sessionID,  
  7.                 'Content-Type' : 'text/html; charset=UTF-8'  
  8.             });  
  9.             res.end();  
  10.         } else {  
  11.             // 处理首页,刷新一条sessionID和二维码  
  12.             generateIndex(sessionID, req, res);  
  13.         }  
  14.     }  

也就是说当直接访问/的时候,服务器强制将请求重定向并包含sessionID信息
[javascript]  view plain copy
  1. function generateIndex(sessionID, req, res) {  
  2.   
  3.     fs.readFile('./index.html''UTF-8'function(err, data) {  
  4.         data = data.replace(/SESSIONID/g, sessionID);  
  5.         res.writeHead(200, {  
  6.             'Content-Type' : 'text/html; charset=UTF-8'  
  7.         });  
  8.         res.end(data);  
  9.     });  
  10.   
  11. }  

当访问的地址符合/?sessionID的时候,服务器读取一个html页面,并将其中的二维码和long polling需要的参数替换为sessionID
[html]  view plain copy
  1. <html>  
  2.     <head>  
  3.         <script src="http://code.jquery.com/jquery-1.6.4.min.js"></script>  
  4.         <script>  
  5.               
  6.             var poll = function() {  
  7.                 $.getJSON('/poll?SESSIONID', function(response) {  
  8.                     var cmd = response.cmd;  
  9.                     if (cmd == 'scanned') {  
  10.                         scanned();  
  11.                     } else if (cmd == 'pclogin') {  
  12.                         var username=response.username;  
  13.                         pclogin(username);  
  14.                     }  
  15.                     poll();  
  16.                 });  
  17.             }  
  18.             var pclogin = function(username) {  
  19.                 $('#output').text('欢迎您:' + username + ',您已成功登录');  
  20.             }  
  21.             var scanned = function() {  
  22.                 $('#output').text('已成功扫描,等待手机确认登录');  
  23.             }  
  24.             poll();  
  25.         </script>  
  26.     </head>  
  27.     <body>  
  28.         <p align="center"><img src="/qrcodeimage?SESSIONID">  
  29.         </p>  
  30.           
  31.     </body>  
  32. </html>  
二维码的请求在我们第二篇就已经介绍过,这里不再重复。
那么维持long polling的接口
[javascript]  view plain copy
  1. if (path == '/poll') {  
  2.         // console.log('polling');  
  3.         var sessionID = url_parts.query;  
  4.         var sessionObj = {  
  5.             'sessionID' : sessionID,  
  6.             'res' : res  
  7.         };  
  8.         clients.push(sessionObj);  
  9.         console.log('client added' + sessionObj);  
  10.     }  

在处理接收客户端完成扫描和确认登录的时候,逻辑比较类似,都是先验证用户的token是否存在,商用的话可能还要有些更安全的考虑
然后根据sessionID找到维持long polling的客户端对象,并且返回相关的操作指令
[javascript]  view plain copy
  1. function handleScanned(res, token, sessionID) {  
  2.     // console.log(">>>" + token + "," + sessionID);  
  3.     var success = false;  
  4.     if (typeof (token) != "undefined") {  
  5.         // 验证是否包含用户信息已确认是登录的用户  
  6.         var userName;  
  7.         redisClient.hget(redisKey, token, function(err, reply) {  
  8.             userName = reply;  
  9.             // console.log("username=" + userName);  
  10.             if (typeof (userName) != "undefined") {  
  11.                 // 用户存在  
  12.                 for ( var int = 0; int < clients.length; int++) {  
  13.                     var clientobj = clients[int];  
  14.                     var savedSession = clientobj.sessionID;  
  15.                     var client = clientobj.res;  
  16.                     if (savedSession == sessionID) {  
  17.                         // 页面存在  
  18.                         client.end(JSON.stringify({  
  19.                             cmd : 'scanned'  
  20.                         }));  
  21.                         clients.splice(int, 1);  
  22.                         success = true;  
  23.                         break;  
  24.                     }  
  25.                 }  
  26.             }  
  27.             res.writeHead(200, {  
  28.                 'Content-Type' : 'text/html; charset=UTF-8'  
  29.             });  
  30.             if (success) {  
  31.                 res.end("scanned");  
  32.             } else {  
  33.                 res.end("error");  
  34.             }  
  35.         });  
  36.   
  37.     }  
  38.   
  39. }  

至此,我们的完整的二维码扫描登录的流程就已经走完了。
放在服务器上运行一下,完全OK,如果想作为daemon的话可以使用forever包。

经过这几篇的介绍,我们不难发现其实这个效果的实现并不是很复杂,关键在于你要把整个逻辑理顺和想清楚。
同时由于这个案例涉及的技术也较多,技术不全面的话也很难形成完成的解决方案。

思考:
这个案例中还存在哪些问题
  1. 微信27秒是事出有因的,考虑到http请求有可能在客户端因为长时间无响应而被终止,因此27秒自动刷新long polling可以有效的防治连接断掉,而在我们这个案例里,并没有去实现这个功能。首先我觉得实现起来没有问题,不难,另外,这些点应该由你们自己去实现,我更加关注的是分析业务。
  2. 关于页面session的内涵,应该可以附加一些加密的信息,对于客户端只是传递这些信息,因此不涉及解密操作,而服务器端就可以验证sessionID的合法性,目前如果你访问/?的时候自己宿便敲sessionID也是可以的,服务器没有做任何验证和限制。
  3. 关于long polling客户端的response对象的维持和清理,在本例中我们直接采用了js的数组进行存储,因此每次都是遍历。如果商用,必然用采用哈希的方式来存储,同时可能还必须存储在数据库内。
  4. 本例只是在客户端确认登录之后在页面上显示确认登录,并没有跳转到某页面,但是实际应用的时候,可能会携带某个服务器生成的钥匙去redirect到某个url,只有目标地址确认这个钥匙是登录确认信息之后才会以某用户方式登录,这个还是希望大家能实现,逻辑很简单,只是本例略掉。

原文连接:http://blog.csdn.net/otangba/article/details/8273952


转载于:https://my.oschina.net/jeffzhao/blog/107934

相关文章:

微软洪小文:AI将成为人类未来最好的左脑

演讲嘉宾 | 洪小文整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;导读&#xff1a;2019 年 6 月 14 日&#xff0c;由清华大学五道口金融学院、清华大学国家金融研究院、清华大学研究生会联合主办的“未来已来—全球领袖论天下”系列讲座再次开讲。应清华…

计算机视觉相关网站

转自&#xff1a;http://blog.sciencenet.cn/home.php?modspace&uid454498&doblog&id377338 1、OpenCV中文网站 http://www.opencv.org.cn/index.php/%E9%A6%96%E9%A1%B5 2、Advanced Digital Imaging Solutions Laboratory (ADISL) Image Apprentice is a C/C ba…

预告 · Flutter Live 2018 全球同步直播

Flutter Live 2018 是 Google 在伦敦线下举办&#xff0c;并面向全球线上直播的一次 Flutter 庆祝活动。在 2018 年已经过去的这段时间里&#xff0c;Flutter 有着非常大的进展&#xff1a; 2 月底在世界移动大会 (MWC) 上宣布了第一个 Beta 版发布;5 月的 Google I/O 大会上发…

context-param与init-param的区别与作用

<context-param>与<init-param>的区别与作用 spring2009-11-04 16:49阅读39 评论0字号&#xff1a;大 中 小<context-param>的作用:web.xml的配置中<context-param>配置作用1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件web.xml.读两个…

C#中object的使用

转自&#xff1a;http://www.hackvip.com/article/sort0129/sort0143/Hackvip_233655.html C#中system.object的函数方法功能介绍 在C#中&#xff0c;Object类型是所有类型的根&#xff0c;大家平常开发中都要跟它打交道&#xff0c;但不见得对它里面的每个方法都知根知底&am…

百炼智百炼智能获5000万元Pre-A轮融资,深耕智能获客赛道

出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;2019年7月9日&#xff0c;百炼智能正式宣布完成5000万元Pre-A轮融资。该轮融资由东方嘉富领投&#xff0c;上市公司任子行、元投资本和酷我音乐创始人雷鸣等投资者跟投。百炼智能利用自有核心自然语言处理、图像识别和…

阿里巴巴连任 Java 全球管理组织席位

百度智能云 云生态狂欢季 热门云产品1折起>>> 11 月 23 日&#xff0c;阿里巴巴宣布连任 Java 全球管理组织 JCP 最高执行委员会委员&#xff0c;任期从 2018 年 12 月 4 号开始&#xff0c;为期两年。阿里表示&#xff0c;这意味将有更多中国开发者的声音被引入 Ja…

Django ModelForm操作及验证

一、内容回顾 Model- 数据库操作- 验证class A(MOdel): user email pwd Form - class LoginForm(Form): email fields.EmailField() user pwd - is_valid -> 每一个字段进行正则(字段内置正则)clean_字段 -> clean(__all__) -> _post_clean - cleand_data - err…

matlab外部接口简介

1、MATLAB外部接口主要包括3部分内容&#xff1a; (1)、MEX文件&#xff1a;外部程序调用接口&#xff1b; MEX文件是MATLAB解释器可以自动加载和运行的动态链接过程&#xff0c;MATLAB可以像调用内部函数一样调用它们。用户通过MEX文件可以完成以下功能&#xff1a; 可以在…

IE调试网页之一:F12 开发人员工具简介

F12 开发人员工具是可帮助生成和调试网页的一套工具。 编写出色的网页需要编码知识以及适当的工具来发现和调试难免会出现的问题。Windows Internet Explorer 9 提供所呈现代码的视图&#xff0c;F12 工具提供 Internet Explorer 9 如何在代码级别上解释这些页面的视图。F12 工…

100万奖金池,这不仅仅是场比赛

这&#xff0c;不仅仅是场比赛&#xff0c;更是对最前沿领域的共同探索2019 E起AI&#xff01;2019年度的大赛&#xff0c;由香港科大商学院和香港科大商学院内地办事处主办&#xff0c;由香港科大EMBA校友企业安讯科技冠名&#xff0c;将围绕人工智能领域的创新及运用展开赛事…

举例说明使用MATLAB Coder从MATLAB生成C/C++代码步骤

MATLAB Coder可以从MATLAB代码生成独立的、可读性强、可移植的C/C代码。 使用MATLAB Coder产生代码的3个步骤&#xff1a;准备用于产生代码的MATLAB算法&#xff1b;检查MATLAB代码的兼容性(有些matlab代码语句并不能生成c/c代码)&#xff1b;产生最终使用的源代码或MEX。 利…

媒体智能应用落地靠5G,视频社交需要想象力

作者简介&#xff1a;卢迪&#xff0c;中国传媒大学新媒体研究院书记、副教授、硕士研究生导师。人工智能正逐渐成为重要的基础设施&#xff0c;在与各行各业传统领域紧密结合的基础上对社会生产、生活方式带来深刻的影响。中央多次强调媒体融合&#xff0c;“要探索将人工智能…

堆栈的链表实现

2019独角兽企业重金招聘Python工程师标准>>> /** stack3.c** Created on: Dec 6, 2012* Author: fsxchen* 链式结构的栈*/ #include #include #include #include typedef struct StackNode //节点结构体 {int data; //存放数…

registry ---------仓库 -----------------镜像

registry --------->仓库 ----------------->镜像    本地镜像都保存在宿主机下 :    /var/lib/docker/containers    镜像从仓库下载下来.镜像保存在仓库中,而仓库存在于Registry中.    Docker Hub 中有两种类型的仓库:    用户库:    用户仓库…

BigBiGAN问世,“GAN父”都说酷的无监督表示学习模型有多优秀?

作者 | Jeff Donahue、Karen Simonyan 译者 | Lucy、一一出品 | AI开发者大本营&#xff08;ID:rgznai100&#xff09;众所周知&#xff0c;对抗训练生成模型&#xff08;GAN&#xff09;在图像生成领域获得了不凡的效果。尽管基于GAN的无监督学习方法取得了初步成果&#xff0…

技术人生:与其鸟宿檐下,不如击翅风雨

人生途中&#xff0c;有些是无法逃避的&#xff0c;比如命运&#xff1b;有些是无法更改的&#xff0c;比如情缘&#xff1b;有些是难以磨灭的&#xff0c;比如记忆&#xff1b;有些是难以搁置的&#xff0c;比如爱恋……与其被动地承受&#xff0c;不如勇敢地面对&#xff1b;…

C++递归用法

转自&#xff1a;http://bbs.ikaka.com/showtopic-664019.aspx 简单谈谈C 递归的思想实现以及和循环的关系 很多初学者往往对递归迷惑不解&#xff0c;也在这上面花了不少的时间。其实教材上的例子很经典&#xff0c;只是它说的有一些唠叨了。初学者会看的头大的。编程是解决…

java导入excle表格,并且对表格进行相应的修改,并对表格数据进行整理,最后导出本地表格等一系列...

1.首先创建一个java项目 完成效果如下图所示 2.导入以下jar包 3.代码如下 其中行和列的操作是根据需求自动划分的 复制代码1 public class auto_date {2 private static List<List<String>> readExcel(File file) throws Exception {3 // 创建输入流&#xff0c;读…

RetinaFace,最强开源人脸检测算法

作者 | CV君 来源 | 我爱计算机视觉&#xff08;ID&#xff1a;aicvmlaicvmlaicvml&#xff09;人脸检测为目标检测的特例&#xff0c;是商业化最早的目标检测算法&#xff0c;也是目前几乎各大 CV 方向 AI 公司的必争之地。WIDER FACE 数据集是由香港中文大学发布的大型人脸数…

OpenCV中cvBlobsLib的编译与使用

OpenCV的cvBlobsLib库的作用类似于matlab中的regionprops函数。 cvBlobsLib库的编译&#xff1a; 首先从http://opencv.willowgarage.com/wiki/cvBlobsLib#Blobextractionlibrary下载最新的v8.3版本的源代码&#xff0c;其次机子上要装有OpenCV1.0的环境&#xff0c;从http:/…

AWS开源Firecracker,一种运行多租户容器服务的新虚拟化技术

现在的技术环境下&#xff0c;容器具有快速启动时间和高密度&#xff0c;VM可以对硬件虚拟化&#xff0c;具有更好的安全性&#xff0c;并对工作负载具有更好的隔离性。容器和VM的特性现在还不可兼得。 现在AWS开源了Firecracker&#xff0c;一种利用KVM的新虚拟化技术&#xf…

python urllib2 开启调试

2019独角兽企业重金招聘Python工程师标准>>> 发一段在网上看见. USING HTTPLIB.HTTPCONNECTION.SET_DEBUGLEVEL() WITH URLLIB2 Posted on October 1, 2007, 9:52 pm, by jamiegrove, under python. I’ve been trying to get the debug level turned on in urll…

从发展滞后到不断突破,NLP已成为AI又一燃爆点?

作者 | 刘家俊&#xff0c;一览群智CTO责编 | Jane出品 | AI科技大本营&#xff08;ID &#xff1a;rgznai100&#xff09;自然语言处理&#xff1a;人工智能连接主义复兴浪潮中的下一个突破口AI 行业应用是一片新的大陆&#xff0c;深度学习作为新大陆的基石&#xff0c;经历了…

matlab最小分类错误全局二值化算法

转自&#xff1a;http://download.csdn.net/detail/hupeng810/1511870 function imagBW kittlerMet(imag) % KITTLERMET binarizes a gray scale image imag into a binary image % Input: % imag: the gray scale image, with black foreground(0), and white % bac…

XShell连接Deepin

为什么80%的码农都做不了架构师&#xff1f;>>> 先让deepin安装openssh sudo apt-get install openssh-serverchkconfig ssh on 转载于:https://my.oschina.net/enzo/blog/110518

第三届“达观杯”文本智能信息抽取挑战赛丰厚奖金,群英集结,等你来战!...

近日&#xff0c;第三届“达观杯”文本智能信息抽取挑战赛正式上线启动&#xff08;点击阅读原文&#xff0c;跳转报名页面&#xff09;&#xff0c;6月28日至8月31日&#xff0c;面向所有参赛选手开放竞赛结果提交。本届“达观杯”的任务是信息抽取。“达观杯”大赛由国内文本…

Spline interpolation and Savitzki-Golay smoothing

转自&#xff1a;http://octave.1599824.n4.nabble.com/Spline-interpolation-and-Savitzki-Golay-smoothing-td1675136.html ## natural-cubic-spline interpolation ## usage: yspline spline(x,y,xspline) ## example: ## x 0:10; y sin(x); ## xspline 0:0.1:10; y…

SpringBoot实现热部署(修改class不需要重启)

热部署: devtools可以实现页面热部署(即页面修改后会立即生效&#xff0c; 这个可以直接在application.properties文件中配置spring.thymeleaf.cachefalse来实现) 实现类文件热部署(类文件修改后不会立即生效),实现对属性文件的热部署。 注意&#xff1a;因为采用的虚拟机机制&…

Oracle中查看表空间的使用率的脚本

如题&#xff1a; select f.tablespace_name tablespace_name, round((d.sumbytes / 1024 / 1024 / 1024), 2) total_g, round(f.sumbytes / 1024 / 1024 / 1024, 2) free_g, round((d.sumbytes - f.sumbytes) / 1024 / 1024 / 1024, 2) used_g, round((d.sumbytes - f.sumbyte…