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

前言

首先,我们看一下微信开放文档中的一张图:上面的一幅图中清楚地介绍了微信登录整个过程,下面对图上所示进行总结:

一、二维码的获得

  1. 用户打开登录网页后,登录网页后台根据微信OAuth2.0协议向微信开发平台请求授权登录,并传递事先在微信开发平台中审核通过的AppIDAppSecrect等参数;

  2. 微信开发平台对AppID等参数进行验证,并向登录网页后台返回二维码;

  3. 登录网页后台将二维码传送至前台进行显示;

二、微信客户端授权登录

  1. 用户使用微信客户端扫描二维码并授权登录;

  2. 微信客户端将二维码特定的uid与微信账号绑定,传送至微信开发平台;

  3. 微信开发平台验证绑定数据,调用登录网页后台的回调接口,发送授权临时票据code

三、网页后台请求数据

  1. 登录网页后台接收到code,表明微信开发平台同意数据请求;

  2. 登录网页后台根据code参数,再加上AppIDAppSecret请求微信开发平台换取access_token

  3. 微信开发平台验证参数,并返回access_token

  4. 登录网页后台收到access_token后即可进行参数分析获得用户账号数据。

实现

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

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

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

1`let http = require("http"); 2let express = require("express"); 3let qrcode = require("qr-image"); 4let app = express(); 5let path = require("path"); 6let server = http.createServer(app); 7let url = require("url"); 8let fs = require("fs"); 9let UUID = require("uuid-js"); 10let generateHTML = null; 11 12app.use(express.static("./public")); 13 14/* 15 * Description: 读取网页文件,用于替换关键字,相当于简易模板 16 * Params: 17 * sessionID - 生成的uid 18 * req - 网页请求 19 * res - 网页应答 20 * fileName - 网页文件所在路径 21 */ 22generateHTML = 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 */ 49let 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 */ 89getJSONValue = 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// 显示网站首页 108app.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// 生成二维码图片并显示 117app.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// 显示手机扫描后的确认界面 139app.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// 在确认界面操作的响应 170app.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请求 207app.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 235server.listen(4000); 236console.log( 237  "Express server listening on port %d in %s mode", 238  server.address().port, 239  app.settings.env 240); 241`

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

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

结语

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

效果图如下:

  1. 登录网页

  1. 登录授权页

  • 欢迎关注我的公众号 前端历劫之路

  • 回复关键词 电子书 ,即可获取12本前端热门电子书

  • 回复关键词 红宝书第4版 ,即可获取最新《JavaScript高级程序设计》(第四版)电子书

  • 关注公众号后,点击下方菜单即可加我微信,我拉拢了很多IT大佬,创建了一个技术交流、文章分享群,期待你的加入

- END -

本文转转自微信公众号前端历劫之路原创https://mp.weixin.qq.com/s/w4cBaLQoRAAUlOHRpcmf_A,如有侵权,请联系删除。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

thinkcmf+jsapi 实现微信支付

首先从小程序端接收订单号、金额等参数,然后后台进行统一下单,把微信支付的订单号返回,在把订单号发送给前台,前台拉起支付,返回参数后更改支付状态。。。回调publicfunctionnotify(){$wechatDb::name('wechat')where('status',1)find();