node實(shí)現(xiàn)小程序登錄全過程(登錄+身份驗(yàn)證+返回token)

ChasenKaos原創(chuàng)文章,轉(zhuǎn)載請注明出處。
根據(jù)官方api的顯示,小程序的登錄流程如下:


1、用戶打開小程序,后臺(tái)自動(dòng)獲取code
2、開發(fā)者把"code"發(fā)送到服務(wù)器。
3、服務(wù)器把"code"、appid、secret等信息發(fā)送到微信官方接口。
4、官方返回一個(gè)session_key和用戶唯一的身份表示openid。
5、開發(fā)者通過官方返回的數(shù)據(jù)生成私有認(rèn)證token。
6、將token返回至客戶端。
7、用戶每次請求攜帶token。

完整代碼如下(node):

1、小程序app.js代碼

//app.js
//app.js
App({
  onLaunch: function() {
    // 展示本地存儲(chǔ)能力
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
    // 獲取用戶信息
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已經(jīng)授權(quán),可以直接調(diào)用 getUserInfo 獲取頭像昵稱,不會(huì)彈框
          wx.getUserInfo({
            success: res => {
              // 可以將 res 發(fā)送給后臺(tái)解碼出 unionId
              this.globalData.userInfo = res.userInfo

              // 由于 getUserInfo 是網(wǎng)絡(luò)請求,可能會(huì)在 Page.onLoad 之后才返回
              // 所以此處加入 callback 以防止這種情況
              if (this.userInfoReadyCallback) {
                this.userInfoReadyCallback(res)
              }
            }
          })
        }
      }
    })
  },
  globalData: {
    userInfo: null
  },
  globalApi: {
    checkUser: 'http://localhost:4000/checkUser/'
  },
  getToken() {
    return new Promise((resolve, reject) => {
      // 登錄
      wx.login({
        success: res => {
          // 發(fā)送 res.code 到后臺(tái)換取 openId, sessionKey, unionId
          if (res.code) {
            //發(fā)送res.code 到后臺(tái)
            wx.request({
              url: this.globalApi.checkUser,
              method: 'POST',
              data: {
                code: res.code
              },
              success(res) {
                //成功返回?cái)?shù)據(jù)后,將token值存儲(chǔ)到localStorage中
                wx.setStorage({
                  key: 'yerlLocalToken',
                  data: res.data.token
                });
                var resArg = res.data.token;
                resolve()
              },
              fail() {
                reject();
              }
            })
          }
        }
      })
    })
  }
})

我是在本地環(huán)境測試的,所以api用的是本地地址,我把它放到了globalApi中方便調(diào)用。

2、小程序的頁面中調(diào)用token

///index.js
//獲取應(yīng)用實(shí)例
const app = getApp()
Page({
  data: {
    token:''
  },
  onLoad() {
    let that = this;
    app.getToken().then(function(){
      that.setData({
        token: wx.getStorageSync('yerlLocalToken')
      });
      console.log(that.data.token)
    });
  }
})

3、后臺(tái)(node + express + mongodb)

//express
const express = require('express');
const router = express.Router();

//處理formdata
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
const urlencodedParser = bodyParser.urlencoded({
    extended: false
});

//處理node request請求
const request = require('request');

//token
const jwt = require('./jwt.js');  //這個(gè)是jsonwebtoken中的方法,我又進(jìn)行了二次加工。

//微信小程序設(shè)置
const wx = require('./wxconfig.json'); //文件中存儲(chǔ)了appid 和 secret

//數(shù)據(jù)庫
const datebase = require('./dbconfig.json') //文件中存儲(chǔ)了數(shù)據(jù)庫地址
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;

//路由
router.post('/', urlencodedParser, (req, res) => {
        //拿到前臺(tái)給的code后,發(fā)送請求
    if(req.body.code) {
        let options = {
            method: 'POST',
            url: 'https://api.weixin.qq.com/sns/jscode2session?',
            formData: {
                appid: wx.appid,
                secret: wx.secret,
                js_code: req.body.code,
                grant_type: 'authorization_code'
            }
        };

        request(options, (error, response, body) => {
            if(error) { //請求異常時(shí),返回錯(cuò)誤信息
                res.json({
                    "status": "error",
                    "code": "ChasenKaso原創(chuàng)文章,轉(zhuǎn)載請注明出處"
                })
            } else {
                //返回值的字符串轉(zhuǎn)JSON
                let _data = JSON.parse(body);

                //根據(jù)返回值創(chuàng)建token
                let _l = jwt.createHoursToken(req.body.code, _data.openid, _data.session_key);
                let _s = jwt.createMonthToken(req.body.code, _data.openid, _data.session_key);

                //連接數(shù)據(jù)庫
                MongoClient.connect(datebase.url, (err, client) => {
                    if(err) {
                        res.json({
                            "status": "error",
                            "code": "0002"
                        });
                        client.close();
                    } else {
                        const users = client.db(datebase.db).collection('Users'); //查詢數(shù)據(jù)庫中是否有當(dāng)前的openid
                        users.count({
                            "openid": _data.openid
                        }, (err, result) => {
                            //當(dāng)數(shù)據(jù)庫中沒有該openid時(shí),插入。
                            if(result == 0) {
                                users.insert({
                                    "openid": _data.openid,
                                    "localToken": _l,
                                    "serverToken": _s
                                }, (err, result) => {
                                    res.json({
                                        "status": "ok",
                                        "token": _l
                                    });
                                    client.close();

                                })
                            };
                            //當(dāng)數(shù)據(jù)庫中查詢到openid時(shí),更新token
                            if(result != 0) {
                                users.update({
                                    "openid": _data.openid
                                }, {
                                    $set: {
                                        "localToken": _l,
                                        "serverToken": _s
                                    }
                                }, (err, result) => {
                                    if(err) {
                                        res.json({
                                            "status": "error",
                                            "code": "0003"
                                        });
                                        client.close();
                                    } else {
                                        res.json({
                                            "status": "ok",
                                            "token": _l
                                        });
                                        client.close();
                                    }
                                })
                            }
                        })
                    }
                })

            }
        });
    } else {
        res.json({
            "status": "error",
            "code": "0004"
        });
    }
})

module.exports = router;

在主程序中調(diào)用這個(gè)router就可以了,代碼如下:

const checkUser = require('./router/checkUser.js');
app.use('/checkUser',checkUser)

運(yùn)行后,顯示如下:


調(diào)試臺(tái)已經(jīng)顯示了后臺(tái)返回的token。
我把a(bǔ)pp.js中的用戶登錄使用promise進(jìn)行了封裝,這樣可以讓我在運(yùn)行小程序并打開index頁面時(shí),可以正常顯示token,否則會(huì)出現(xiàn)異步登錄未成功,頁面就已經(jīng)渲染完成,無法獲取到token的現(xiàn)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載鏈接 注:本文轉(zhuǎn)載知乎上的回答 作者:初雪 鏈接:https://www.zhihu.com/question...
    pengshuangta閱讀 29,326評論 9 295
  • 盛夏的一個(gè)午后,孩子們和家人都睡著了,我偷偷一個(gè)人來到離家不遠(yuǎn)的一家小超市買了一只巧克力冰淇淋,躲在屋檐下的一角,...
    林秋千閱讀 1,391評論 0 1
  • 周末時(shí)光好美妙, 瓊漿滋養(yǎng)身心好。 靜臥床榻親莫笑, 窗外雨濯綠絲絳。
    寧靜遇夏雨閱讀 207評論 1 5
  • 《超時(shí)空同居》講述了佟麗婭飾演的來自2018年的失意女青年谷小焦,因?yàn)橐粋€(gè)突如其來的時(shí)空意外,不得不與雷佳音飾演的...
    辛小婷閱讀 531評論 0 4
  • 我想要的感情,從來都是平平淡淡,細(xì)水長流。 我想要的感情,不過是相濡以沫。回首一笑,眼睛里,心里,都是歡喜。 小的...
    慕白橙閱讀 322評論 0 0

友情鏈接更多精彩內(nèi)容