1. 程式人生 > >[小程式NO.1]如何在小程式開發工具提供的Node.js快速啟動模版的基礎上獲得微信群資訊

[小程式NO.1]如何在小程式開發工具提供的Node.js快速啟動模版的基礎上獲得微信群資訊

    LZ最近在做一個基於群的小程式,需要先解決如何獲得小程式提供的微信群資訊的技術問題。由於LZ之前沒有建站經驗,所以直接使用了騰訊雲的小程式解決方案,就是Node.js快速啟動模版。這個模版後臺是基於koa框架和微信自己開發的wafer2框架。其中wafer2框架中直接提供了對於使用者登陸、上傳圖片等常見功能的解決方案。大家可以跳到這裡檢視,https://www.jianshu.com/p/072ff89e723c。但是,不幸的是:wafer2框架中並沒有對於獲得群資訊的封裝,所以我們要自己實現。

        根據官方文件中的open-data部分的介紹,為了保護使用者隱私,小程式只向開發者提供了openGid。它用來在某個特定小程式中唯一標識一個群,並且可以通過open-data元件來向用戶展示群名稱(並不能拿到群名稱只是展示而已)。可以先看這篇文章:https://www.ifanr.com/minapp/832760  看完之後,大家可以基本理解這個流程。本文主要介紹通過1044狀態碼進入小程式(即通過其他群成員分享到群中的小程式小卡片進入)如何拿到群資訊。

一、第一條線索:檢視官方文件關於轉發的部分,通過1044進入時,小程式就可以額外獲取到 shareTicket。有了 shareTicket,我們就可以通過呼叫 wx.getShareInfo 函式,獲取到目標微信群的encryptedData和iv。注意這是用來獲得群id的關鍵線索。那麼我們接著看官方文件:https://developers.weixin.qq.com/miniprogram/dev/api/signature.html#wxchecksessionobject。這裡提供瞭解密演算法的下載。然後我們將它下載下來並開啟檢視node部分。解密演算法如下所示:

var crypto = require('crypto')

function WXBizDataCrypt(appId, sessionKey) {
  this.appId = appId
  this.sessionKey = sessionKey
}

WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) {
  // base64 decode
  var sessionKey = new Buffer(this.sessionKey, 'base64')
  encryptedData = new Buffer(encryptedData, 'base64')
  iv = new Buffer(iv, 'base64')
  try {
     // 解密
    var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv)
    // 設定自動 padding 為 true,刪除填充補位
    decipher.setAutoPadding(true)
    var decoded = decipher.update(encryptedData, 'binary', 'utf8')
    decoded += decipher.final('utf8')
    decoded = JSON.parse(decoded)
  } catch (err) {
    throw new Error('Illegal Buffer')
  }
  if (decoded.watermark.appid !== this.appId) {
    throw new Error('Illegal Buffer')
  }
  return decoded
}
module.exports = WXBizDataCrypt

給出的demo樣例如下所示:

var WXBizDataCrypt = require('./WXBizDataCrypt')
var appId = 'wxee6e3f1721fc1c61'
var sessionKey = 'Sc7PC6lZ3IPgcx7h+ZQMvA=='
var encryptedData="7deXSrzADGzvcwWjhWYded3UHMtusTG9kiX5jW14uSSFfbyzqW5xD3SP2x0r+ZSBuMJMCkxGHsgIEBvTDuKAeo5D44hdcIFv9OoYB/pexWDElpVqqxhHvtH22hax2qr3pro4fX2R2Tz6Vw3CL1ZaTQ=="
var pc = new WXBizDataCrypt(appId, sessionKey)
var data = pc.decryptData(encryptedData , iv)
console.log('解密後 data: ', data)

這裡我們可以得知:只要為這個演算法提供四個輸入:appid,session_key,encryptedData和iv,它便可以將openGid返回給我們。問題似乎變得清晰了。

二、那麼我們分別通過什麼方式可以得到appid,session_key,encryptedData和iv呢?

1、首先是appid,我們開啟騰訊雲提供的phpadmin資料庫操作介面,可以發現在cAuth資料庫的cAppinfo表中儲存了appid。GOT IT!!如下所示


2、session_key:這個比較複雜,首先我們需要對小程式的登陸流程有所理解,大家可以參見官方文件https://developers.weixin.qq.com/miniprogram/dev/api/api-login.html。通過文件,我們瞭解到,要拿到session_key,我們需要拿到前端攜帶在http頭部的第三方skey,然後在cAuth資料庫中檢視cSessionInfo表,用第三方的skey來拿到session_key
3、然後是encryptedData和iv了,它們在小程式app的onshow或onlaunch方法中,可以拿到,如下所示,我們拿到後,使用wx.setStorageSync將其儲存下來
App({
    onLaunch: function (ops) {
        qcloud.setLoginUrl(config.service.loginUrl)
        if (ops.scene == 1044) {
          console.log(ops.shareTicket)
          wx.getShareInfo({
            shareTicket: ops.shareTicket,
            complete(res) {
              console.log(res)
              wx.setStorageSync('encryptedData', res.encryptedData)
              wx.setStorageSync('iv', res.iv)
            }
          })
        }
    }
})

我們可以通過小程式開發工具對儲存下來的值進行檢視


然後通過qcloud.request方法,將它們放到options.data物件中傳送給後端即可,然後在success回撥函式中,將openGid賦值給data.open_gid,如下所示

doGroup:function(){
      var that=this
      util.showBusy('傳送群資訊中...')
      var options ={ 
        url:config.service.groupUrl,
        login:false,
        // method:'POST',
        data:{
          encrytedData: wx.getStorageSync('encryptedData'),
          iv:wx.getStorageSync('iv')
        },
        success(result) {
          util.showSuccess('請求成功完成')
          console.log('request success', result)
          that.setData({ open_gid: result.data.data.openGId})
          console.log(that.data.open_gid)
        },
        fail(error) {
          util.showModel('請求失敗', error);
          console.log('request fail', error);
        }
      }
      qcloud.request(options)
    },


三、後臺拿到後,直接呼叫我們前面下載的解碼函式解碼即可得到openGid,具體程式碼如下:

1、在page.json的dependencies中新增對解碼演算法使用的crypto包的依賴如下圖所示:


2、在route資料夾的index.js下新增如下程式碼:

router.get('/group', validationMiddleware,controllers.group)//[msg|這裡填寫路由,和轉發給誰進行處理]

3、將前文中的解碼演算法複製到controllers資料夾下(這種做法不夠好,但可以解決問題)

4、再在controllers資料夾中新建group.js,輸入如下程式碼

var WXBizDataCrypt = require('./WXBizDataCrypt')
const qcloud = require('../qcloud')
const { mysql } = qcloud
module.exports = async (ctx, next) => {
  if (ctx.state.$wxInfo.loginState === 1) {
    // loginState 為 1,登入態校驗成功
    try{
      const appInfo = await mysql('cAppinfo').select('*')//[msg|從資料庫取得appInfo物件]
      const appId=appInfo[0].appid//[msg|從appInfo上拿到appId的值]
      const { 'x-wx-skey': skey } = ctx.req.headers//[msg|從請求頭拿到skey]
      const sessionInfo = await mysql('cSessionInfo').select('*').where({ skey: skey })//[msg|根據skey從資料庫拿到]
      const sessionKey = sessionInfo[0].session_key//[msg|根據skey拿到sessionkey]
      const encryptedData = ctx.query.encrytedData//[msg|從query中拿到encryedData]
      const iv = ctx.query.iv//[msg|從query中拿到iv]
      const pc = new WXBizDataCrypt(appId, sessionKey)//[msg|初始化並呼叫解碼器來解碼]
      const data = pc.decryptData(encryptedData, iv)
      ctx.state.data = data//[msg|將得到的資料放到請求體中用於發給前端]
    }catch(e){
      ctx.state.code = -1
    }
  } else {
    ctx.state.code = -1
  }
}

四、寫在最後面(LZ目前沒有解決的問題)

1、LZ發現:在測試環境下,使用手機將測試版小程式分享到群中,每次拿到的openGid都是不同的

2、更不幸的是:將這個openGid繫結至open-data,如下所示,並不能獲得群名稱(所以我這七個小時都幹了什麼)

<open-datatype='groupName'open-gid='{{open_gid}}'></open-data>

3、然後,使用微信web開發工具,通過下圖方式編譯進入,每次拿到的openGid都不一樣

4、所以,在開發環境下,lz並沒有解決openGid的問題,而且似乎也不知道該如何解決(如果有大神知道,請指教)