手寫一個仿微信登入的Nodejs程式
阿新 • • 發佈:2021-08-16
前言
首先,我們看一下微信開放文件中的一張圖:
上面的一幅圖中清楚地介紹了微信登入整個過程,下面對圖上所示進行總結:
一、二維碼的獲得
使用者開啟登入網頁後,登入網頁後臺根據微信OAuth2.0協議向微信開發平臺請求授權登入,並傳遞事先在微信開發平臺中稽核通過的AppID和AppSecrect等引數;
微信開發平臺對AppID等引數進行驗證,並向登入網頁後臺返回二維碼;
登入網頁後臺將二維碼傳送至前臺進行顯示;
二、微信客戶端授權登入
使用者使用微信客戶端掃描二維碼並授權登入;
微信客戶端將二維碼特定的uid與微信賬號繫結,傳送至微信開發平臺;
微信開發平臺驗證繫結資料,呼叫登入網頁後臺的回撥介面,傳送授權臨時票據code;
三、網頁後臺請求資料
登入網頁後臺接收到code,表明微信開發平臺同意資料請求;
登入網頁後臺根據code引數,再加上AppID和AppSecret請求微信開發平臺換取access_token;
微信開發平臺驗證引數,並返回access_token;
登入網頁後臺收到access_token後即可進行引數分析獲得使用者賬號資料。
https://www.houdianzi.com/ logo設計公司
實現
瞭解了大致原理之後,我們就開始簡單實現這個邏輯。因為沒有直接呼叫微信開發平臺,所以這裡只是演示效果。你也可以通過訪問:
https://www.maomin.club/qrcodelogin/
這個我的線上網址體驗一下。以下程式碼是主要邏輯,結合線上網址體驗更容易理解。
lethttp=require("http");
letexpress=require("express");
letqrcode=require("qr-image");
letapp=express();
letpath=require("path");
letserver=http.createServer(app);
leturl=require("url");
letfs=require("fs");
letUUID=require("uuid-js");
letgeneratehtml=null;
app.use(express.static("./public"));
/*
*Description:讀取網頁檔案,用於替換關鍵字,相當於簡易模板
*Params:
*sessionID-生成的uid
*req-網頁請求
*res-網頁應答
*fileName-網頁檔案所在路徑
*/
generatehtml=function(sessionID,req,res,fileName){
fs.readFile(fileName,"UTF-8",function(err,data){
if(!err){
data=data.replace(/SESSION_UID/g,sessionID);
res.writeHead(200,{
"Content-Type":"text/html;charset=UTF-8",
});
res.end(data);
}else{
console.log(err);
res.writeHead(404,{
"Content-Type":"text/html;charset=UTF-8",
});
res.end();
}
});
};
/*
*Description:寫入js ON檔案
*Params:
*fileName-JSON檔案所在路徑
*uid-生成的uid
*writeData-需要寫入的JSON格式資料
*
*/
letsetJSONValue=function(fileName,uid,writeData){
letdata=fs.readFileSync(fileName);
letusers=JSON.parse(data.toString());
letaddFlag=true;
letdelFlag=writeData===null;
for(leti=0;i<users.data.length;i++){
if(users.data[i].uid===uid){
addFlag=false;
if(delFlag){
users.data.splice(i,1);
}else{
users.data[i].status=writeData.status;
console.log(
"writeJSON:"+JSON.stringify(users.data[i])+"modified."
);
}
}
}
if(addFlag){
users.data.push(writeData);
console.log("writeJSON:"+JSON.stringify(writeData)+"inserted.");
}
//同步寫入檔案
letwriteJSON=JSON.stringify(users);
fs.writeFileSync(fileName,writeJSON);
};
/*
*Description:讀取JSON檔案(要返回資料,選擇同步讀取)
*Params:
*fileName-JSON檔案所在路徑
*uid-生成的uid
*
*/
getJSONValue=function(fileName,uid){
letreadData=null;
//同步讀取檔案
letdata=fs.readFileSync(fileName);
letusers=JSON.parse(data.toString());
for(leti=0;i<users.data.length;i++){
if(users.data[i].uid===uid){
readData=JSON.stringify(users.data[i]);
break;
}
}
returnreadData;
};
//顯示網站首頁
app.get("/",function(req,res){
//生成唯一的ID
letuid=UUID.create();
console.log("uid:'"+uid+"'generated.");
//替換網頁模板內的UID關鍵字
generateHTML(uid,req,res,path.join(__dirname,"/views/main.html"));
});
//生成二維碼圖片並顯示
app.get("/qrcode",function(req,res,next){
letuid=url.parse(req.url,true).query.uid;
try{
if(typeofuid!=="undefined"){
//寫入二維碼內的網址,微信掃描後自動跳轉。下面的網址是我的網址,https://www.maomin.club/qrcodelogin,你可以換成自己的線上網址或者本地伺服器。加上後面的"/scanned?uid="
letjumpURL="https://www.maomin.club/qrcodelogin/scanned?uid="+uid;
//生成二維碼(size:圖片大小,margin:邊框留白)
letimg=qrcode.image(jumpURL,{size:6,margin:2});
res.writeHead(200,{"Content-Type":"image/png"});
img.pipe(res);
}else{
res.writeHead(414,{"Content-Type":"text/html"});
res.end("<h1>414Request-URITooLarge</h1>");
}
}catch(e){
res.writeHead(414,{"Content-Type":"text/html"});
res.end("<h1>414Request-URITooLarge</h1>");
}
});
//顯示手機掃描後的確認介面
app.get("/scanned",function(req,res){
letuid=url.parse(req.url,true).query.uid;
if(typeofuid!=="undefined"){
generateHTML(uid,req,res,path.join(__dirname,"/views/confirm.html"));
console.log("uid:'"+uid+"'scanned.");
//獲取JSON檔案內對應uid的資料,更改其資料狀態
letjsonData=getJSONValue(path.join(__dirname,"/bin/data.json"),uid);
if(jsonData===null){
jsonData={
uid:uid,
status:"scanned",
name:"USER",
};
}else{
jsonData=JSON.parse(jsonData);
jsonData.status="scanned";
}
//寫入JSON檔案
setJSONValue(path.join(__dirname,"/bin/data.json"),uid,jsonData);
}else{
res.writeHead(414,{"Content-Type":"text/html"});
res.end("<h1>414Request-URITooLarge</h1>");
}
});
//在確認介面操作的響應
app.get("/confirmed",function(req,res){
letuid=url.parse(req.url,true).query.uid;
letoperate=url.parse(req.url,true).query.operate;
if(typeofuid!=="undefined"){
console.log("uid:'"+uid+"'"+operate);
letjsonData=getJSONValue(path.join(__dirname,"/bin/data.json"),uid);
letstatus=operate==="confirm"?"verified":"canceled";
if(jsonData===null){
jsonData={
uid:uid,
status:status,
name:"USER",
};
}else{
jsonData=JSON.parse(jsonData);
jsonData.status=status;
}
setJSONValue(path.join(__dirname,"/bin/data.json"),uid,jsonData);
if(status==="verified"){
res.writeHead(200,{"Content-Type":"text/html"});
res.end("<h1>登入成功!</h1>");
}else{
res.writeHead(200,{"Content-Type":"text/html"});
res.end("<h1>Canceled!</h1>");
}
}else{
res.writeHead(414,{"Content-Type":"text/html"});
res.end("<h1>414Request-URITooLarge</h1>");
}
});
//響應主頁不斷的AJAX請求
app.get("/verified",function(req,res){
letuid=url.parse(req.url,true).query.uid;
//normal-沒有任何觸發
//scanned-已掃描
//canceled-已取消
//verified-已驗證
letdataStatus={
cmd:"normal",
user:"",
};
console.log("uid:'"+uid+"'query...");
if(typeofuid!=="undefined"){
letuserData=getJSONValue(path.join(__dirname,"/bin/data.json"),uid);