今天我們來實作一個使用個人訂閱號實作網站的功能,后端使用的是 express ,其它框架原理基本一致,只是定義路由或回傳回應資料部分代碼跟 express 有所出入,先來一波效果圖:
1. 前言
20 年 3 月在掘金寫過一篇文章,介紹了使用 express 開發微信公眾號的案例: 原文地址,當時使用的 nodejs 版本還是 v8.x,現如今,nodejs 的最新長期穩定版已經來到了 v18.16.0, 新特性嘗鮮版更是已經到了 v20.1.0,
不得不感慨時間之飛逝,nodejs 的版本升級之路見證了技術的飛速進步,也見證了我發際線的飛速退后,
......
2. 為何要用訂閱號去登錄
看了看掘金,發現掘金的登錄注冊分為兩種:
- 手機號驗證碼注冊
- 第三方登錄(支持微信掃碼、微博、Github)
企業站來說,這種方式固然好,給了用戶提供了更多的選擇,
但是對于個人站點,這種方式就不太友好了,因為:
手機號接收驗證碼注冊:1 條短信 1 毛錢,10 條就是 1 塊,100 條就能吃個豪華早餐了
接入微信開放平臺是需要企業資質的,我一個打工人,上哪兒去搞企業資質?這就是一道坎,更別說后續接入的繁瑣流程了,
Github 倒是不需要,不過接入也麻煩,棄之!
微博?用戶沒有微博賬號是不是還得去注冊個微博賬號?棄!
那就普通的注冊吧?填寫用戶名、密碼、確認密碼、輸入郵箱,郵箱驗證碼確認?棄!
那么,它來了!訂閱號注冊免費,花極少的時間去接入
用戶只需關注公眾號,反手輸入個登錄,回車!Ctrl + CV,欸,很快啊,登錄成功!
相對于傳統的注冊填一大堆資料 + 確認密碼 + 驗證碼,孰好孰壞,熟快熟慢,一試便知,
而且,且看下圖,它對于保護用戶隱私來說,是極好的,因為開發者只能拿到用戶的 OpenID,頭像和昵稱都不給你!
不過, 有 OpenID 就足夠了,畢竟它對于當前公眾號來說,是 唯一的
3. 公眾號后臺配置
完整的接入指南詳見: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
進入微信公眾平臺
設定與開發 -> 基本配置 -> 公眾號開發資訊 -> IP 白名單中配置你的服務器 IP
設定與開發 -> 基本配置 -> 服務器配置(未開啟的需要先開啟)
-
URL是你的后端服務對應驗證授權的介面地址,也就是你后端服務部署后系結了域名的介面地址,舉例:https://www.xxx.com/wechat -
Token這塊的Token按照平臺提示的規則自定義即可,注意,需要和上面代碼中的Token相對應,不然會驗證不成功, -
EncodingAESKey是訊息加解密密鑰,這個可以隨機生成,也可以自定義 -
訊息加解密方式選擇明文模式即可
填寫好上面這些內容之后,我們先不必急著點擊提交,因為這個時候服務還沒部署,服務器是無法正常回應微信服務器的驗證請求,必然會提交失敗,
且看下文
4. 登錄流程梳理
我們簡單的梳理一下登錄流程:
-
首先是需要在網站需要登錄的地方,放置一個公眾號的二維碼,并用醒目的文字給用戶提示:關注公眾號后發送 "登錄" ,獲取登錄所需要的驗證碼
-
后臺接收到用戶請求,判斷用戶發送的內容的確是 "登錄" 關鍵字,回傳一個驗證碼給用戶,并將當前驗證碼存起來
-
用戶輸入驗證碼,后臺根據存起來的驗證碼進行驗證,驗證成功,回傳登錄成功,否則,登錄失敗
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 處理用戶 "登錄" 訊息
接下來我們定義用戶發送訊息的邏輯:
當用戶發送文本訊息給公眾號的時候,微信服務器會將用戶發送的訊息以 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位隨機驗證碼,再將驗證碼和用戶的OpenID以key(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 提交公眾號配置
服務部署成功后,回到我們的 公眾號后臺配置 部分,點擊提交即可
結語
至此,我們的個人訂閱號登錄功能就已經完成了,相信基于以上,大家都可以很快的去做出一個網站登錄功能出來
而且在給用戶提供服務的同時,可以直接將用戶引導至自己的公眾號上,不管是后面對網站的更新記錄,還是一些重要的通知,都可以通過公眾號進行訊息推送,一舉兩得
好了,今天的文章分享就到這里了,如果對大家有所幫助的話,希望您不要吝嗇手中的贊呦~
正式給大家介紹一下:
基于 OpenAI 的 API 開發的一款 ChatGPT 網站,模型是gpt-3.5-turbo,使用的是本篇文章的同款登錄方式,歡迎大家體驗
免費!免費!免費!https://ai.iiter.cn
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/552859.html
標籤:JavaScript
上一篇:利用HTML5存盤物件:localStorage和sessionStorage決議
下一篇:返回列表
