1. 程式人生 > 其它 >手寫一個仿微信登入的Nodejs程式

手寫一個仿微信登入的Nodejs程式

前言

首先,我們看一下微信開放文件中的一張圖:

上面的一幅圖中清楚地介紹了微信登入整個過程,下面對圖上所示進行總結:

一、二維碼的獲得

使用者開啟登入網頁後,登入網頁後臺根據微信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); //返回JSON資料用於首頁AJAX操作 if(userData!==null){ userData=JSON.parse(userData); dataStatus.cmd=userData.status; dataStatus.user=userData.name; } } res.end(JSON.stringify(dataStatus)); }); server.listen(4000); console.log( "Expressserverlisteningonport%din%smode", server.address().port, app.settings.env );