手写一个仿微信登录的Nodejs程序

网络 通信技术
本篇教你手写一个仿微信登录的Nodejs程序,希望对你有所帮助。

[[357291]]

 前言

首先,我们看一下微信开放文档中的一张图:

上面的一幅图中清楚地介绍了微信登录整个过程,下面对图上所示进行总结:

一、二维码的获得

  1. 用户打开登录网页后,登录网页后台根据微信OAuth2.0协议向微信开发平台请求授权登录,并传递事先在微信开发平台中审核通过的AppID和AppSecrect等参数;
  2. 微信开发平台对AppID等参数进行验证,并向登录网页后台返回二维码;
  3. 登录网页后台将二维码传送至前台进行显示;

二、微信客户端授权登录

  1. 用户使用微信客户端扫描二维码并授权登录;
  2. 微信客户端将二维码特定的uid与微信账号绑定,传送至微信开发平台;
  3. 微信开发平台验证绑定数据,调用登录网页后台的回调接口,发送授权临时票据code;

三、网页后台请求数据

  1. 登录网页后台接收到code,表明微信开发平台同意数据请求;
  2. 登录网页后台根据code参数,再加上AppID和AppSecret请求微信开发平台换取access_token;
  3. 微信开发平台验证参数,并返回access_token;
  4. 登录网页后台收到access_token后即可进行参数分析获得用户账号数据。

实现

了解了大致原理之后,我们就开始简单实现这个逻辑。因为没有直接调用微信开发平台,所以这里只是演示效果。你也可以通过访问:

  1. https://www.maomin.club/qrcodelogin/ 

这个我的线上网址体验一下。以下代码是主要逻辑,结合线上网址体验更容易理解。

  1. let http = require("http"); 
  2. let express = require("express"); 
  3. let qrcode = require("qr-image"); 
  4. let app = express(); 
  5. let path = require("path"); 
  6. let server = http.createServer(app); 
  7. let url = require("url"); 
  8. let fs = require("fs"); 
  9. let UUID = require("uuid-js"); 
  10. let generateHTML = null
  11.  
  12. app.use(express.static("./public")); 
  13.  
  14. /* 
  15.  * Description: 读取网页文件,用于替换关键字,相当于简易模板 
  16.  * Params: 
  17.  * sessionID - 生成的uid 
  18.  * req - 网页请求 
  19.  * res - 网页应答 
  20.  * fileName - 网页文件所在路径 
  21.  */ 
  22. generateHTML = function (sessionID, req, res, fileName) { 
  23.   fs.readFile(fileName, "UTF-8"function (err, data) { 
  24.     if (!err) { 
  25.       data = data.replace(/SESSION_UID/g, sessionID); 
  26.       res.writeHead(200, { 
  27.         "Content-Type""text/html; charset=UTF-8"
  28.       }); 
  29.       res.end(data); 
  30.     } else { 
  31.       console.log(err); 
  32.  
  33.       res.writeHead(404, { 
  34.         "Content-Type""text/html; charset=UTF-8"
  35.       }); 
  36.       res.end(); 
  37.     } 
  38.   }); 
  39. }; 
  40.  
  41. /* 
  42.  * Description: 写入JSON文件 
  43.  * Params: 
  44.  * fileName - JSON文件所在路径 
  45.  * uid - 生成的uid 
  46.  * writeData - 需要写入的JSON格式数据 
  47.  * 
  48.  */ 
  49. let setJSONValue = function (fileName, uid, writeData) { 
  50.   let data = fs.readFileSync(fileName); 
  51.  
  52.   let users = JSON.parse(data.toString()); 
  53.   let addFlag = true
  54.   let delFlag = writeData === null
  55.  
  56.   for (let i = 0; i < users.data.length; i++) { 
  57.     if (users.data[i].uid === uid) { 
  58.       addFlag = false
  59.  
  60.       if (delFlag) { 
  61.         users.data.splice(i, 1); 
  62.       } else { 
  63.         users.data[i].status = writeData.status; 
  64.  
  65.         console.log( 
  66.           "writeJSON: " + JSON.stringify(users.data[i]) + " modified." 
  67.         ); 
  68.       } 
  69.     } 
  70.   } 
  71.  
  72.   if (addFlag) { 
  73.     users.data.push(writeData); 
  74.     console.log("writeJSON: " + JSON.stringify(writeData) + " inserted."); 
  75.   } 
  76.  
  77.   // 同步写入文件 
  78.   let writeJSON = JSON.stringify(users); 
  79.   fs.writeFileSync(fileName, writeJSON); 
  80. }; 
  81.  
  82. /* 
  83.  * Description: 读取JSON文件(要返回数据,选择同步读取) 
  84.  * Params: 
  85.  * fileName - JSON文件所在路径 
  86.  * uid - 生成的uid 
  87.  * 
  88.  */ 
  89. getJSONValue = function (fileName, uid) { 
  90.   let readData = null
  91.  
  92.   // 同步读取文件 
  93.   let data = fs.readFileSync(fileName); 
  94.  
  95.   let users = JSON.parse(data.toString()); 
  96.  
  97.   for (let i = 0; i < users.data.length; i++) { 
  98.     if (users.data[i].uid === uid) { 
  99.       readData = JSON.stringify(users.data[i]); 
  100.       break; 
  101.     } 
  102.   } 
  103.  
  104.   return readData; 
  105. }; 
  106.  
  107. // 显示网站首页 
  108. app.get("/"function (req, res) { 
  109.   // 生成唯一的ID 
  110.   let uid = UUID.create(); 
  111.   console.log("uid: '" + uid + "' generated."); 
  112.   // 替换网页模板内的UID关键字 
  113.   generateHTML(uid, req, res, path.join(__dirname, "/views/main.html")); 
  114. }); 
  115.  
  116. // 生成二维码图片并显示 
  117. app.get("/qrcode"function (req, res, next) { 
  118.   let uid = url.parse(req.url, true).query.uid; 
  119.  
  120.   try { 
  121.     if (typeof uid !== "undefined") { 
  122.       // 写入二维码内的网址,微信扫描后自动跳转。下面的网址是我的网址,https://www.maomin.club/qrcodelogin ,你可以换成自己的线上网址或者本地服务器。加上后面的"/scanned?uid=" 
  123.       let jumpURL = "https://www.maomin.club/qrcodelogin/scanned?uid=" + uid; 
  124.       // 生成二维码(size:图片大小, margin: 边框留白) 
  125.       let img = qrcode.image(jumpURL, { size: 6, margin: 2 }); 
  126.       res.writeHead(200, { "Content-Type""image/png" }); 
  127.       img.pipe(res); 
  128.     } else { 
  129.       res.writeHead(414, { "Content-Type""text/html" }); 
  130.       res.end("<h1>414 Request-URI Too Large</h1>"); 
  131.     } 
  132.   } catch (e) { 
  133.     res.writeHead(414, { "Content-Type""text/html" }); 
  134.     res.end("<h1>414 Request-URI Too Large</h1>"); 
  135.   } 
  136. }); 
  137.  
  138. // 显示手机扫描后的确认界面 
  139. app.get("/scanned"function (req, res) { 
  140.   let uid = url.parse(req.url, true).query.uid; 
  141.  
  142.   if (typeof uid !== "undefined") { 
  143.     generateHTML(uid, req, res, path.join(__dirname, "/views/confirm.html")); 
  144.  
  145.     console.log("uid: '" + uid + "' scanned."); 
  146.  
  147.     // 获取JSON文件内对应uid的数据,更改其数据状态 
  148.     let jsonData = getJSONValue(path.join(__dirname, "/bin/data.json"), uid); 
  149.  
  150.     if (jsonData === null) { 
  151.       jsonData = { 
  152.         uid: uid, 
  153.         status: "scanned"
  154.         name"USER"
  155.       }; 
  156.     } else { 
  157.       jsonData = JSON.parse(jsonData); 
  158.       jsonData.status = "scanned"
  159.     } 
  160.  
  161.     // 写入JSON文件 
  162.     setJSONValue(path.join(__dirname, "/bin/data.json"), uid, jsonData); 
  163.   } else { 
  164.     res.writeHead(414, { "Content-Type""text/html" }); 
  165.     res.end("<h1>414 Request-URI Too Large</h1>"); 
  166.   } 
  167. }); 
  168.  
  169. // 在确认界面操作的响应 
  170. app.get("/confirmed"function (req, res) { 
  171.   let uid = url.parse(req.url, true).query.uid; 
  172.   let operate = url.parse(req.url, true).query.operate; 
  173.  
  174.   if (typeof uid !== "undefined") { 
  175.     console.log("uid: '" + uid + "' " + operate); 
  176.  
  177.     let jsonData = getJSONValue(path.join(__dirname, "/bin/data.json"), uid); 
  178.     let status = operate === "confirm" ? "verified" : "canceled"
  179.  
  180.     if (jsonData === null) { 
  181.       jsonData = { 
  182.         uid: uid, 
  183.         status: status, 
  184.         name"USER"
  185.       }; 
  186.     } else { 
  187.       jsonData = JSON.parse(jsonData); 
  188.       jsonData.status = status; 
  189.     } 
  190.  
  191.     setJSONValue(path.join(__dirname, "/bin/data.json"), uid, jsonData); 
  192.  
  193.     if (status === "verified") { 
  194.       res.writeHead(200, { "Content-Type""text/html" }); 
  195.       res.end("<h1 style='textAlign:center;'>登录成功!</h1>"); 
  196.     } else { 
  197.       res.writeHead(200, { "Content-Type""text/html" }); 
  198.       res.end("<h1 style='textAlign:center;'>Canceled!</h1>"); 
  199.     } 
  200.   } else { 
  201.     res.writeHead(414, { "Content-Type""text/html" }); 
  202.     res.end("<h1 style='textAlign:center;'>414 Request-URI Too Large</h1>"); 
  203.   } 
  204. }); 
  205.  
  206. // 响应主页不断的AJAX请求 
  207. app.get("/verified"function (req, res) { 
  208.   let uid = url.parse(req.url, true).query.uid; 
  209.  
  210.   // normal   - 没有任何触发 
  211.   // scanned  - 已扫描 
  212.   // canceled - 已取消 
  213.   // verified - 已验证 
  214.   let dataStatus = { 
  215.     cmd: "normal"
  216.     user""
  217.   }; 
  218.  
  219.   console.log("uid: '" + uid + "' query ..."); 
  220.  
  221.   if (typeof uid !== "undefined") { 
  222.     let userData = getJSONValue(path.join(__dirname, "/bin/data.json"), uid); 
  223.  
  224.     // 返回JSON数据用于首页AJAX操作 
  225.     if (userData !== null) { 
  226.       userData = JSON.parse(userData); 
  227.       dataStatus.cmd = userData.status; 
  228.       dataStatus.user = userData.name
  229.     } 
  230.   } 
  231.  
  232.   res.end(JSON.stringify(dataStatus)); 
  233. }); 
  234.  
  235. server.listen(4000); 
  236. console.log( 
  237.   "Express server listening on port %d in %s mode"
  238.   server.address().port, 
  239.   app.settings.env 
  240. ); 

看到这里,你是不是觉得代码不够全,咋就给了一个主要逻辑代码,别着急,代码满汉全席马上奉上,代码解释可以看注释哦!以下是github网址,如果觉得对自己有用,欢迎star~

  1. https://github.com/maomincoding/qrcodelogin.git 

结语

看到这里了,你可能直接拉取代码,发现项目咋运行不了呢?效果也不跟线上网址那样。是这样的,如果你有线上服务器,可以把它部署到云端。如果没有线上服务器,你可以自己搭建一个本地局域网服务器。一定要保证手机跟电脑网页在一个IP网段上。

效果图如下:

登录网页


登录授权页


 

责任编辑:姜华 来源: 前端历劫之路
相关推荐

2016-09-30 09:22:55

2015-09-22 10:43:37

微信雷达

2019-03-19 19:19:19

Facebook微信转型

2012-12-24 13:25:59

微信App

2013-04-01 13:15:49

微信微信公众账号微信推广

2015-08-07 15:39:26

仿微信语音界面源码

2016-02-15 11:47:54

微信源码下拉视频

2021-03-04 11:50:48

微信Spring Secu登录

2021-10-26 00:25:14

程序登录流程

2022-03-09 09:43:01

工具类线程项目

2022-10-31 08:27:53

Database数据数据库

2021-02-22 17:17:38

Proxy缓存代码

2021-01-29 18:02:52

企业微信私域流量

2013-08-19 15:00:53

微信易信

2021-11-23 10:00:55

鸿蒙HarmonyOS应用

2013-08-20 15:22:47

2015-09-01 16:55:42

微信朋友圈图片

2020-11-02 08:19:18

RPC框架Java

2021-03-18 08:04:54

AQS工具CAS

2021-12-07 06:55:17

节流函数Throttle
点赞
收藏

51CTO技术栈公众号