大师网-带你快速走向大师之路 解决你在学习过程中的疑惑,带你快速进入大师之门。节省时间,提升效率

node实现小程序登录全过程(登录+身份验证+返回token)

ChasenKaos原创文章,转载请注明出处。
根据官方api的显示,小程序的登录流程如下:


1、用户打开小程序,后台自动获取code
2、开发者把"code"发送到服务器。
3、服务器把"code"、appid、secret等信息发送到微信官方接口。
4、官方返回一个session_key和用户唯一的身份表示openid。
5、开发者通过官方返回的数据生成私有认证token。
6、将token返回至客户端。
7、用户每次请求携带token。

完整代码如下(node):

1、小程序app.js代码

//app.js
//app.js
App({
  onLaunch: function() {
    // 展示本地存储能力
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
    // 获取用户信息
    wx.getSetting({
      success: res => {
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
            success: res => {
              // 可以将 res 发送给后台解码出 unionId
              this.globalData.userInfo = res.userInfo

              // 由于 getUserInfo 是网络请求,可能会在 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 => {
          // 发送 res.code 到后台换取 openId, sessionKey, unionId
          if (res.code) {
            //发送res.code 到后台
            wx.request({
              url: this.globalApi.checkUser,
              method: 'POST',
              data: {
                code: res.code
              },
              success(res) {
                //成功返回数据后,将token值存储到localStorage中
                wx.setStorage({
                  key: 'yerlLocalToken',
                  data: res.data.token
                });
                var resArg = res.data.token;
                resolve()
              },
              fail() {
                reject();
              }
            })
          }
        }
      })
    })
  }
})

我是在本地环境测试的,所以api用的是本地地址,我把它放到了globalApi中方便调用。

2、小程序的页面中调用token

///index.js
//获取应用实例
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、后台(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');  //这个是jsonwebtoken中的方法,我又进行了二次加工。

//微信小程序设置
const wx = require('./wxconfig.json'); //文件中存储了appid 和 secret

//数据库
const datebase = require('./dbconfig.json') //文件中存储了数据库地址
const mongodb = require('mongodb');
const MongoClient = mongodb.MongoClient;

//路由
router.post('/', urlencodedParser, (req, res) => {
        //拿到前台给的code后,发送请求
    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) { //请求异常时,返回错误信息
                res.json({
                    "status": "error",
                    "code": "ChasenKaso原创文章,转载请注明出处"
                })
            } else {
                //返回值的字符串转JSON
                let _data = JSON.parse(body);

                //根据返回值创建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);

                //连接数据库
                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'); //查询数据库中是否有当前的openid
                        users.count({
                            "openid": _data.openid
                        }, (err, result) => {
                            //当数据库中没有该openid时,插入。
                            if(result == 0) {
                                users.insert({
                                    "openid": _data.openid,
                                    "localToken": _l,
                                    "serverToken": _s
                                }, (err, result) => {
                                    res.json({
                                        "status": "ok",
                                        "token": _l
                                    });
                                    client.close();

                                })
                            };
                            //当数据库中查询到openid时,更新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;

在主程序中调用这个router就可以了,代码如下:

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

运行后,显示如下:


调试台已经显示了后台返回的token。
我把app.js中的用户登录使用promise进行了封装,这样可以让我在运行小程序并打开index页面时,可以正常显示token,否则会出现异步登录未成功,页面就已经渲染完成,无法获取到token的现象。