微信小程序搭配后端实现用户注册登录颁发token凭证

微信小程序登录逻辑

在这里插入图片描述
说明:
调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
注意:
会话密钥 session_key 是对用户数据进行加密签名的密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。
临时登录凭证 code 只能使用一次。

个人开发思路

微信小程序端,通过wx.login()可以获取到一个临时的登录凭证,然后将这个code发送到服务端,服务端将code与配置信息(appid、appsecret)一同发送到微信服务端换取用户的唯一凭证openid(相当于QQ号,每个用户独一无二)。如果这个时候用户不存在,那就创建,然后将openid存入数据库中再返回token给用户。要是用户存在就直接返回登录的token给用户。

微信小程序端

小程序配置 ./config/config.js

module.exports = {
  api: 'http://localhost:3000/mini/api',
}

小程序最主要界面app.js相关配置

const config = require('./config/config.js');

App({
  globalData: {
    info:''
  },
  onLaunch: function () {
    this.logIn();
  },
  logIn: function () {
    return new Promise(function (resolve, reject) {
      wx.login({
        success: (res) => {
          let code = res.code
          if (code) {
                wx.setStorageSync('user', res.userInfo)
                wx.request({
                  url: `${config.api}/login`,
                  method: 'POST',
                  data: {
                    code: code
                  },
                  header: {
                    'content-type': 'application/json'
                  },
                  success: function (res) {
                    console.log(res.data.token)
                    wx.setStorageSync('token', res.data.token)
                    resolve(res)
                  }
                })
          } else {
            console.log('获取用户登录态失败!' + res.errMsg)
            reject(err)
          }
        }
      });
    })
    
  },
  checkToken: function () {
    console.log('Promise');
    const token = wx.getStorageSync('token')
    return new Promise(function (resolve, reject) {
      wx.request({
        url: `${config.api}/auth`,
        header: {
          'authorization': token
        },
        method: 'POST',
        success(res) {
          console.log(res);
          resolve(res)
        },
        fail(err) {
          console.log(err);
          reject(err)
        }
      })
    })
  }
})

服务端

创建数据库模型 Quser.js

const mongoose = require('mongoose')

const UserSchema = new mongoose.Schema(
    {
        openId: { type: String },       //唯一Openid标识
        nickname: { type: String },     //用户昵称
        unionid: { type: String },
        headimgurl: { type: String },   //用户头像
        gender: { type: String },       //用户性别,1男2女
    },
    { timestamps: true }
)

module.exports = mongoose.model('Quser', UserSchema)

创建路由模块 mini/index.js 和 quser.js
首先我们看index.js

module.exports = app => {
    const express = require('express')
    const router = express.Router()
    const qusers = require('./quser')
    //检测token是否过期
    router.post('/auth', qusers.checkToken)
    //换取token
    router.post('/login', qusers.login)
    //初始化用户资料
    router.post('/user', qusers.checkToken, async (req, res) => {
        const model = await req.Model.create(req.body)
        res.send(model)
    })
    app.use('/mini/api/', async (req, res, next) => {
        next()
    }, router)
}

我们接着看quser.js

const jwt = require('jsonwebtoken')
const User = require('../../models/Quser')
const Qdetile = require('../../models/Qdetile')
const System = require('../../models/System')

const config = require('../../plugins/config')
const axios = require('axios')

let generateToken = function (user) {
    return jwt.sign(user, config.jwtSecret, {
        expiresIn: 7200
    })
}
//获取页面初始化资料
exports.now = async (req, res) => {
    const queryOptions = {}
    const model = await System.find().setOptions(queryOptions)
    return res.json({
        model
    })
}

exports.login = (req, res) => {
    const queryString = `appid=${config.appId}&secret=${config.appSecret}&js_code=${req.body.code}&grant_type=authorization_code`;
    const wxAPI = `https://api.weixin.qq.com/sns/jscode2session?${queryString}`;
    axios.get(wxAPI)
        .then(response => {
            User.findOne({ openId: response.data.openid }, (err, user) => {
                if (user) {
                    console.log(user)

                    return res.json({
                        token: generateToken({ openid: response.data.openid })
                    })
                } else {
                    const user = new User();
                    user.openId = response.data.openid;
                    user.save();
                    return res.json({
                        token: generateToken({
                            openid: response.data.openid
                        })
                    })
                }
            })
        })
        .catch(error => {
            console.log(error)
        })
}

exports.checkToken = (req, res, next) => {
    let token = req.headers.authorization;
    console.log(token);
    if (token) {
        console.log('token exist');
        jwt.verify(token, config.jwtSecret, (err, decoded) => {
            console.log('jwt.verify');
            if (err) {
                console.log('err');
                if (err.name === 'TokenExpiredError') {
                    console.log('认证码失效,请重新登录!');
                    return res.status(401).json({ error: '认证码失效,请重新登录!' });
                } else {
                    console.log('认证失败!');
                    return res.status(401).json({ error: '认证失败!' });
                }
            } else {
                if (decoded.openid) {
                    req.openid = decoded.openid;
                    console.log('req.openid = decoded.openid;');
                    return res.status(200).json({ message: '已登录' });
                } else {
                    console.log('认证失败!');
                    res.status(401).json({ error: '认证失败!' });
                }
            }
        });
    } else {
        console.log("no token");
        return res.status(403).json({
            error: '请提供认证码!'
        });
    }
}

config.js 配置信息

module.exports = {
    // db uri
    db: 'mongodb://127.0.0.1:27017/test',
    // Setting port for server
    port: 3000,
    host: 'http://localhost:3000',
    jwtSecret: '',
    appId: '',
    appSecret: '',
}

相关资料

GitHub:BeijiYang/MinaJwtLoginLogout

posted @ 2019-10-21 22:54  南风S  阅读(483)  评论(0)    收藏  举报