Express实战个人订阅号实现网站登录

news2024/11/19 14:32:28

今天我们来实现一个使用个人订阅号实现网站的功能,后端使用的是 express 。其它框架原理基本一致,只是定义路由或返回响应数据部分代码跟 express 有所出入。先来一波效果图:

发送登录关键字,获取验证码

输入验证码,进行验证

1. 前言

20 年 3 月在掘金写过一篇文章,介绍了使用 express 开发微信公众号的案例: 原文地址。当时使用的 nodejs 版本还是 v8.x,现如今,nodejs 的最新长期稳定版已经来到了 v18.16.0, 新特性尝鲜版更是已经到了 v20.1.0

nodejs版本

不得不感慨时间之飞逝,nodejs 的版本升级之路见证了技术的飞速进步,也见证了我发际线的飞速退后。

2. 为何要用订阅号去登录

看了看掘金,发现掘金的登录注册分为两种:

  • 手机号验证码注册
  • 第三方登录(支持微信扫码、微博、Github)

企业站来说,这种方式固然好,给了用户提供了更多的选择。

但是对于个人站点,这种方式就不太友好了,因为:

手机号接收验证码注册:1 条短信 1 毛钱,10 条就是 1 块,100 条就能吃个豪华早餐了

接入微信开放平台是需要企业资质的。我一个打工人,上哪儿去搞企业资质?这就是一道坎,更别说后续接入的繁琐流程了。

Github 倒是不需要,不过接入也麻烦,弃之!

微博?用户没有微博账号是不是还得去注册个微博账号?弃!

那就普通的注册吧?填写用户名、密码、确认密码、输入邮箱,邮箱验证码确认?弃!

那么,它来了!订阅号注册免费,花极少的时间去接入

用户只需关注公众号,反手输入个登录,回车!Ctrl + CV。欸,很快啊,登录成功!

相对于传统的注册填一大堆资料 + 确认密码 + 验证码,孰好孰坏,熟快熟慢,一试便知。

而且,且看下图,它对于保护用户隐私来说,是极好的,因为开发者只能拿到用户的 OpenID,头像和昵称都不给你!

用户信息只能拿到OpenID

不过, 有 OpenID 就足够了,毕竟它对于当前公众号来说,是 唯一的

唯一的OpenID

3. 公众号后台配置

完整的接入指南详见: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html

进入微信公众平台

设置与开发 -> 基本配置 -> 公众号开发信息 -> IP 白名单中配置你的服务器 IP

设置与开发 -> 基本配置 -> 服务器配置(未开启的需要先开启)

image.png

  • URL 是你的后端服务对应验证授权的接口地址,也就是你后端服务部署后绑定了域名的接口地址。举例: https://www.xxx.com/wechat

  • Token 这块的 Token 按照平台提示的规则自定义即可,注意,需要和上面代码中的 Token 相对应,不然会验证不成功。

  • EncodingAESKey 是消息加解密密钥,这个可以随机生成,也可以自定义

  • 消息加解密方式 选择明文模式即可

填写好上面这些内容之后,我们先不必急着点击提交,因为这个时候服务还没部署,服务器是无法正常响应微信服务器的验证请求,必然会提交失败。

且看下文

4. 登录流程梳理

我们简单的梳理一下登录流程:

  1. 首先是需要在网站需要登录的地方,放置一个公众号的二维码,并用醒目的文字给用户提示:关注公众号后发送 “登录” ,获取登录所需要的验证码

  2. 后台接收到用户请求,判断用户发送的内容的确是 “登录” 关键字,返回一个验证码给用户,并将当前验证码存起来

  3. 用户输入验证码,后台根据存起来的验证码进行验证,验证成功,返回登录成功,否则,登录失败

5. 定义服务端路由,处理验证逻辑及服务端部署

5.1 定义授权验证接口

主要是验证消息的确来自微信服务器

import express from "express";
import jsSHA from "jssha";
const router = express.Router();
router.get("/wechat_mp", (req, res, next) => {
  const token = "这里是自定义Token,可自定义,内容规则详见下文";
  //1.获取微信服务器Get请求的参数 signature、timestamp、nonce、echostr
  const { signature, timestamp, nonce, echostr } = req.query;

  //2.将token、timestamp、nonce三个参数进行字典序排序
  const array = [token, timestamp, nonce].sort();

  //3.将三个参数字符串拼接成一个字符串进行sha1加密
  const tempStr = array.join("");
  const shaObj = new jsSHA("SHA-1", "TEXT");
  shaObj.update(tempStr);
  const scyptoString = shaObj.getHash("HEX");

  //4.开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
  if (signature === scyptoString) {
    console.log("验证成功");
    res.send(echostr);
  } else {
    console.log("验证失败");
    res.send("验证失败");
  }
});

5.2 处理用户 “登录” 消息

接下来我们定义用户发送消息的逻辑:

image.png

当用户发送文本消息给公众号的时候,微信服务器会将用户发送的消息以 XML格式 的参数去请求这个接口,只不过这个时候,我们需要通过 POST 请求来接收参数。

先上代码:

定义POST接口

import { parseString } from "xml2js";
import myCache from "../store";
/**
 * 随机6位验证码
 */
function randomCode() {
  return Math.random().toString().slice(-6);
}
/**
 * 回复文字消息封装
 */
function sendTextMsg(toUser, fromUser, content) {
  let resultXml = "<xml><ToUserName><![CDATA[" + fromUser + "]]></ToUserName>";
  resultXml += "<FromUserName><![CDATA[" + toUser + "]]></FromUserName>";
  resultXml += "<CreateTime>" + new Date().getTime() + "</CreateTime>";
  resultXml += "<MsgType><![CDATA[text]]></MsgType>";
  resultXml += "<Content><![CDATA[" + content + "]]></Content></xml>";
  return resultXml;
}
router.post("/wechat_mp", function (req, res) {
  var buffer = [];
  req.on("data", function (data) {
    buffer.push(data);
  });
  // 内容接收完毕
  req.on("end", function () {
    var msgXml = Buffer.concat(buffer).toString("utf-8");
    parseString(msgXml, { explicitArray: false }, function (err, result) {
      if (err) throw err;
      result = result.xml;
      const { ToUserName, FromUserName, MsgType, Content } = result;
      if (MsgType === "text" && Content === "登录") {
        const code = randomCode();
        // 这里的FromUserName就是用户的OpenID
        myCache.set(code, FromUserName, 1 * 60 * 5);
        const sendXml = sendTextMsg(
          ToUserName,
          FromUserName,
          `您的登录验证码是:${code}  ,  有效期为5分钟`
        );
        res.send(sendXml);
      }
    });
  });
});

store/index.js

import NodeCache from "node-cache";
const myCache = new NodeCache();
export default myCache;

代码解释:

  • XML 解析:可以通过安装 xml2js 这个库来解析 XML 格式的参数

  • 接化发:检测到用户发送 消息类型text 且内容为 登录 关键字的时候,我们去生成一个 6位随机验证码 ,再将验证码和用户的 OpenIDkey(code)、value(OpenId) 的格式存入 存入 node-cache 中,并设置有效期为 5分钟,同时将随机验证码发送给用户。

发送登录关键字,获取验证码

5.3 处理网站登录逻辑

上一步,用户已经在微信公众号上获取到了随机验证码,现在只需要在网站需要登录的地方输入验证码,调用验证码校验接口即可进行校验。

输入验证码,进行验证

定义验证码校验接口

import myCache from "../store";
router.get("/verify", async function (req, res) {
  const { code } = req.query;
  const OpenID = myCache.get(code);
  if (OpenID) {
    const token = "使用OpenID进行jwt鉴权颁发Token";
    res.json({
      code: 200,
      data: { token },
    });
  } else {
    res.json({
      code: 400,
      msg: "您输入的验证码有误或已过期,请重新输入!-_-",
    });
  }
});

代码解释:

根据用户输入的验证码,去 node-cache 中获取 OpenID,如果存在,则说明验证码正确,jwt 鉴权颁发 Token。反之,校验失败。

验证码校验成功后,通过唯一的 OpenID 和自定义的 secret 给用户颁发 Token,用户再次访问网站的时候,只需要携带 Token 即可进行鉴权。

关于 jwt 鉴权概念、流程及使用,大家可以参考这条 AI 问答分享:


我在ChatGPTer(https://ai.iiter.cn)网站上创建了一个AI对话分享,快来看看吧!

「jwt鉴权概念、流程」

链接:https://ai.iiter.cn/#/share/6465c5a2e82a696c417bbfa9

同时,也欢迎大家自己进行 AI 问答: https://ai.iiter.cn

5.4 部署服务

服务部署这块大家可以参考我之前写的一篇傻瓜式部署文章:宝塔面板结合 pm2 进程管理工具部署前端项目

当然,您也可以使用自己顺手的部署方式

5.5 提交公众号配置

服务部署成功后,回到我们的 公众号后台配置 部分,点击提交即可

结语

至此,我们的个人订阅号登录功能就已经完成了,相信基于以上,大家都可以很快的去做出一个网站登录功能出来

而且在给用户提供服务的同时,可以直接将用户引导至自己的公众号上。不管是后面对网站的更新记录,还是一些重要的通知,都可以通过公众号进行消息推送,一举两得

好了,今天的文章分享就到这里了,如果对大家有所帮助的话,希望您不要吝啬手中的赞呦~

正式给大家介绍一下:

基于 OpenAIAPI 开发的一款 ChatGPT 网站,模型是gpt-3.5-turbo,使用的是本篇文章的同款登录方式,欢迎大家体验

免费!免费!免费!https://ai.iiter.cn

image.png

image.png

image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用cmake 构建构建新项目的时候,编译提示库找不到怎么办?

昨天帮其他部门同事解决Linux下Qt编译找不到Qt 依赖库 core的问题。过程很有特征性&#xff0c;可以推广到Linux下使用cmake构建项目时找不到库文件的广泛性问题。 先上图&#xff0c;结合事情经过讲述&#xff1a; 事情经过&#xff1a; 这里给大家介绍第一个重点&#xff1…

秘塔写作猫

秘塔写作猫是集AI智能写作、多人协作、改写润色、文本校对等功能为一体的AI原生创作平台&#xff0c;可以帮助不同群体大幅提升写作效率和生产力。 接下来小编就带大家了解一下该软件具体的一些功能&#xff0c;不论你是学生、上班族还是自媒体从业者等&#xff0c;该工具绝对可…

箭头函数与普通的函数有什么区别-M

箭头函数与普通的函数有什么区别 1、写法不同 在 js 中&#xff0c;像命名式函数、函数表达式都称为普通函数。对于普通函数&#xff0c;需要用function关键字来声明。而箭头函数则不需要使用function关键字&#xff0c;在箭头前面的括号里面写参数&#xff0c;后面的大括号里…

Linux 安装MySQL-5.7.30

1.官网下载MySQL 进入官网https://www.mysql.com/ 从下载页面下载社区版本其中社区版本免费&#xff0c;免费的午餐不提供技术支持. 页面中MySQL Enterprise Edition是企业版&#xff0c;企业版收费但是会提供技术支持&#xff0c; 点击图中红框下载社区版本 选择Download Arc…

AC,AP以及三阶段项目

特点&#xff1a;access&#xff1a;连接终端设备 只能通过1个vlan trunk&#xff1a;交换机与交换机相连 可以通过多个vlan 共同特点&#xff1a;交换机的端口收发数据的规则&#xff1a; 收&#xff1a;如果收到的数据&#xff0c;没有携带任何标签&#xff0c;则使用该端口…

01SpringCloudRibbon负载均衡

Ribbon负载均衡 Ribbon Eureka帮我们集成了负载均衡组件&#xff1a;Ribbon&#xff0c;简单修改代码即可使用。 什么是Ribbon&#xff1a;客户端负载均衡组件 开启负载均衡 1、Eureka中已经集成了Ribbon&#xff0c;所以我们无需引入新的依赖&#xff0c;直接修改代码。 2、…

Redis的主从集群搭建与配置

文章目录 Redis主从集群模式搭建过程分级管理容灾冷处理 Redis主从集群模式 Redis的集群模式 主从复制模式&#xff1a;利用主从复制原理&#xff0c;一主多从架构。读写分离&#xff0c;主节点可读可写&#xff0c;从节点只能提供读服务。哨兵模式&#xff1a;哨兵实现了自动化…

Vue 3 + Element Plus 简单用法

Element Plus&#xff1a; A Desktop UI toolkit for Vue.js 即 Vue 桌面 UI 工具包 基于 Vue 2 的组件库和基于 Vue 3 的组件库安装方法不同&#xff0c;基于 Vue 3 的组件库叫做 Element Plus。 MDBootstrap 与 Element UI 区别&#xff1a; MD Bootstrap vs ElementUI: W…

如何在金融企业推进故障演练?中国人寿分阶段实践总结

一分钟精华速览 越来越多企业正在通过故障注入和演练的方式提升系统可靠性&#xff0c;这其中金融行业的应用较为特殊。一方面其可靠性要求比非涉账类系统更高&#xff1b;另一方面金融行业有更加严格的监管要求&#xff0c;如客户、账目等信息都有严格约束。加之金融系统较其…

ActiveMQ基础学习简单记录

ActiveMQ基础学习简单记录 JMS是什么JMS消息模型JMS Message Type Activemq安装概念强化JMS的跨平台性JMS通用接口JMS希望达到的目标是什么 Activemq发送消息的三种模式至少一次至多一次精确一次可重复确认模式小结 Activemq支持众多协议Activemq支持的定时消息,延迟消息,优先级…

【C++技能树】类和对象的使用 --初始化列表,static,友元,内部类,匿名对象的理解与使用

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我bua&#xff01; 类和对象的使用 0. 初始化列表explicit关键字 1.Static静态成员变量2.友元2.1.友元函数2.2.友元类 3.内部类4.匿名对象4.匿名对象至此初始化列表,static…

【Linux】2. Shell运行原理与Linux权限操作

专栏导读 &#x1f341;作者简介&#xff1a;余悸&#xff0c;在读本科生一枚&#xff0c;致力于 C方向学习。 &#x1f341;收录于 C 专栏&#xff0c;本专栏主要内容为 C 初阶、 C 进阶、 STL 详解等&#xff0c;持续更新中&#xff01; &#x1f341;相关专栏推荐&#xff1…

Cloud Studio 有“新”分享

GitHub仓库推荐 Awesome Open Source Applications - 收集了各种开源应用程序&#xff0c;包括 Web 应用、桌面应用、移动应用等。Cloud Studio 一键运行 Free for Dev - 收集了各种免费的开源应用程序和工具&#xff0c;包括 Web 应用、桌面应用、移动应用等。Cloud Studio 一…

kaggle经典赛 | IEEE欺诈检测竞赛金牌方案分享

https://www.kaggle.com/competitions/ieee-fraud-detection 赛题背景 想象一下&#xff0c;站在杂货店的收银台&#xff0c;身后排着长队&#xff0c;收银员不那么安静地宣布你的卡被拒绝了。在这一刻&#xff0c;你可能没有考虑决定你命运的数据科学。 尴尬&#xff0c;并…

一文搞定验证码(上部分)

1.背景 目前收到反馈,存在一类用户,在利用会员权益大量进行二次销售;而且还是自动进行操作的. 那么意味着他们有一个自动平台在对我们的商品进行二次销售. 这是就该我们的主角登场了. 验证码模块可以有效防止机器人刷接口 2.开源验证码框架 通过在网上查找资料, 发现了几个验…

C++:采用哈希表封装unordered_map和unordered_set

目录 一. 如何使用一张哈希表封装unordered_map和unordered_set 二. 哈希表迭代器的实现 2.1 迭代器成员变量及应当实现的功能 2.2 operator函数 2.3 operator*和operator->函数 2.4 operator!和operator函数 2.5 begin()和end() 2.6哈希表迭代器实现代码 三. unord…

渗透测试--6.2.mdk3攻击wifi

前言 本次依然使用Kali虚拟机系统&#xff0c;win11主机&#xff0c;网卡Ralink 802.11 配合mdk3进行wifi伪造、连接设备查看、解除认证攻击。本次实验只用于学习交流&#xff0c;攻击目标为自家的手机热点&#xff0c;请勿违法使用&#xff01; 目录 前言 1.Deauth攻击原…

Electron简介、安装、实践

本文中的所有代码均存放在https://github.com/MADMAX110/my-electron-app Electron是什么&#xff1f; Electron是一个开源的框架&#xff0c;可以使用JavaScript, HTML和CSS来构建跨平台的桌面应用程序。Electron的核心是由Chromium和Node.js组成&#xff0c;它们分别提供了渲…

【springboot 开发工具】接口文档我正在使用它生成,舒坦

前言 先来描述下背景&#xff1a;由于新公司业务属于自研产品开发&#xff0c;但是发现各产品业务线对于接口文档暂时还是通过集成Swagger来维护&#xff0c;准确来说是knife4j&#xff08;Swagger的增强解决方案&#xff09;。但是对于5年的后端开发老说&#xff0c;早就厌倦…

Java-线程安全的四个经典案例和线程池

单例模式 有些对象&#xff0c;在一个程序中应该只有唯一 一个实例&#xff08;光靠人保证不靠谱 借助语法来保证&#xff09; 就可以使用单例模式 在单例模式下 对象的实例化被限制了 只能创建一个 多了的也创建不了 单例模式分为两种&#xff1a;饿汉模式和懒汉模式 饿汉模式…