Nodejs实现微信小程序登陆

2021-01-14
108
383

小程序登陆

小程序可以通过微信官方提供登陆能力方便的获取微信提供的用户身份标识,快速建立小程序内的用户体系。

详情见:微信小程序登陆(官方文档)

官方文档给出的说明可以说是相当“简洁”了,总结起来就两句话:

  1. 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
  2. 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID会话密钥 session_key。 之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

登陆流程图

登录流程图

为了写好这篇文章,凭着自己的感觉把官方文档给出的UML图又“抄”了一遍。

相信你只要愿意按照以下步骤梳理并“抄写”代码,基本上就可以掌握小程序登陆的通用过程了。

1. wx.login( )

wx.login() 这个是微信小程序官方提供的 Api,用来获取登录凭证(code)。通过凭证在开发者服务器调用auth.code2Session , 使用 code 换取用户登录态信息,包括用户的唯一标识(openid)及本次登录会话的密钥(session_key)等。

wx.login({
  success (res) {
    if (res.code) {
      //发起网络请求
      wx.request({
        url: 'https://test.com/onLogin',
        data: {
          code: res.code
        }
      })
    } else {
      console.log('登录失败!' + res.errMsg)
    }
  }
})

2. 获取 openid 和 session_key

  • openid:在微信公众平台中用作用户在每个应用(订阅号、服务号、小程序)之间唯一标识。
  • session_key:验证当前用户行为的有效性,可以用来间接地维护用户在开发者服务端的登录态。
  • Api: https://api.weixin.qq.com/sns/jscode2session

使用字符串拼接或封装请求参数的方式得到以下请求地址 :

GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

小程序发起登陆请求

/**
 * 授权登录
 */
getUserInfo(e) {
    const _this = this
    if (e.detail.errMsg !== 'getUserInfo:ok') {
      return false
    }
    wx.showLoading({
      title: "正在登录",
      mask: true
    })
    // 执行微信登录
    wx.login({
      success(res) {
        // 发送用户信息
        const data = {
          code: res.code,
          user_info: e.detail.rawData,
          encrypted_data: e.detail.encryptedData,
          iv: e.detail.iv,
          signature: e.detail.signature,
        }

        // 小程序的 post 请求根据业务需求可自行封装
        _this._post('users/login', data, res => {
          // 记录token user_id
          if(res.data.token) {
            // 获取token成功,则写入本地缓存
            wx.setStorageSync('token', res.data.token)
            wx.setStorageSync('user_id', res.data.user_id)
            wx.showToast({
                title: '登录成功',
                icon: 'success'
            })
          } else {
            wx.showToast({
              title: '登陆失败了',
              icon: 'none'
            })
          }

        }, fail => {
          // 登陆失败
          wx.showToast({
            title: '登陆失败了',
            icon: 'none'
          })
        }, () => {
          wx.hideLoading()
        })
      }
    })
 }

Nodejs 登录路由

const { getSessionKey } = require('../../controller/UsersController')
// 用户登录 router
router.post('/login', async (ctx, next) => {
    const { code, user_info, encrypted_data, iv, signature } = ctx.request.body
    // appid appSecret 定义在 config.js 中,直接引用过来即可
    const data = await getSessionKey(code, appid, appSecret, user_info)

    ctx.body = { data } // 返回的数据包含 token 和 user_id
})

Nodejs 用户模型控制器

// UserController
const https = require('https')

// 请求 session_key
function getSessionKey (code, appid, appSecret, user_info) {
    const params = `?appid=${appid}&appSecret=${appSecret}&js_code=${code}&grant_type=authorization_code`
    const data = await _httpsGetAsync('https://api.weixin.qq.com/sns/jscode2session' + params)
    // 使用 token 生成器方法加密 微小返回的 session_key 得到自定义 token
    const token = _tokenGenerator(data.session_key)
    redisSet(token, data.session_key) // 存入 redis

    // user_info 是小程序传过来的用户数据,类型为 json 字符串
    const wxUser = JSON.parse(user_info) // 解析微信用户基础数据
    // 使用 ORM 查找该用户的openid,如果数据库中没有,则将该用户入库,如果有该用户,则返回刚才加密后的token 和 这个用户的 id
    // 这里使用的是 Sequelize,也可以是其他 ORM, 也可以是原生 SQL 语句操作数据库,看喜好和需求
    const user = await User.findOrCreate({
        where: {
            openId: data.openid
        },
        defaults: wxUser
    })

    if (user) {
        return { token, user_id: user[0].dataValues.id }
    }
    return null
}

// Async 化 https get 请求
async function _httpsGetAsync (url) {
    // Promise化 https get 请求
    function _promisifyHttpsGet (url) {
        return new Promise((resolve, reject) => {
            https.get(url, (req, res) => {
                let data = ''
                req.on('data', chunk => data += chunk)
                req.on('end', () => resolve(JSON.parse(data)))
            }).on('error', err => reject(err))
        })
    }
    return await _promisifyHttpsGet(url)
}

// token 生成器
function _tokenGenerator (sessionKey) {
    return crypto.createHash('sha1').update(sessionKey, 'utf8').digest('hex')
}
  1. 通过请求 https://api.weixin.qq.com/sns/jscode2sessio 这个Api地址并携带 appid、secret、js_code、grant_type(默认为'authorization_code')四个参数获取该用户的openidsession_key
  2. 通过 _tokenGenerator 封装的 hash 加密函数获取加密session_key得到的token
  3. 用户入库:通过openid字段在数据库查找该用户,如果存在则直接返回token和该用的id,如果不存在该用户,则先将用户信息保存到数据库中再返回得到的用户idtoken
  4. 小程序端接收到包含token的数据包则判定为用户登录成功

_httpsGetAsync 和 _tokenGenerator 应当封装到 utils 目录下,或 service 层去。这里为了读者能直观的理解代码,没有做拆分。

小程序登陆演示

小程序登陆演示

小程序登录返回数据

将返回的 token 和 user_id 存放到(Storage)缓存中,方便后期业务逻辑请求时携带token

扩展 wx.checkSession()

微信服务发放的session_key并不是永久有效的,就像开发者服务器发放的token一样,存在过期时间的说法。那么每次发生业务交互的时候如何判断当前用户的session_key的有效性呢?
wx.checkSession() 方法可以用来直接检查微信服务最近一次发放的用户session_key是否过期

checkSession

wx.checkSession({
    success: (res) => {
    //session_key 未过期,并且在本生命周期一直有效
    },
    fail: (res) => {
    // session_key 已经失效,需要重新执行登录流程
    //重新登录
    },
    complete: (res) => {},
})
108

文章版权所有:PORK's BLOG,采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。

欢迎分享,转载务必保留出处及原文链接