一、全局理解




微信小程序 session_id_微信小程序 session_id


全局信息流程图

问题 1:unionid 有什么用?

答:unionid 是小程序用户的跨程序标识多个小程序绑在同一个微信开放平台时,用户在每个小程序的 unionid 是一样的。

这使多个小程序使用一套用户系统成为可能。

问题 2:如何获得 unionid?

答:(请看上图)

First:通过微信提供的 api:wx.getUserInfo 获取带有 unionid 加密信息的返回数据 res

Second:将返回数据中的成员变量 res.encryptedData 和 res.iv 作为传入参数传入工具类 UserInfoDecrypt 的成员方法 decryptData()

Third:访问工具类的成员变量 UserInfoDecrypt.unionid 获取最终的 unionid

二、工具类 UserInfoDecrypt

问题 3:工具类 UserInfoDecrypt 是用来做什么的?

答:UserInfoDecrypt 是一个基于 CryptoJS 的封装类,CryptoJS 是一个 JavaScript 的用于加密解密的工具库。

由于 wx.getUserInfo 返回来的是加密数据,所以我们需要使用 CryptoJS 帮助我们解密,UserInfoDecrypt 是最终我们要用到的工具。

问题 4:如何使用 UserInfoDecrypt?

答:

第一步:下载 CryptoJS 的包


https://github.com/gwjjeff/cryptojs/archive/master.zip


微信小程序 session_id_sessionkey 微信小程序获取_02

解压后的目录结构


第二步:在 cryptojs-master 文件夹内创建文件 UserInfoDecrypt.js


// UserInfoDecrypt.js
const Crypto = require("/cryptojs.js").Crypto;

function UserInfoDecrypt(appId, sessionKey) {
  this.appId = appId;
  this.sessionKey = sessionKey;
}

UserInfoDecrypt.prototype.decryptData = function(encryptedData, iv) {
  encryptedData = Crypto.util.base64ToBytes(encryptedData);
  iv = Crypto.util.base64ToBytes(iv);
  const key = Crypto.util.base64ToBytes(this.sessionKey);

  const mode = new Crypto.mode.CBC(Crypto.pad.pkcs7);

  try {
    const bytes = Crypto.AES.decrypt(encryptedData, key, {
      asBpytes: true,
      iv: iv,
      mode: mode
    });

    const decryptResult = JSON.parse(bytes);

    if (decryptResult.watermark.appid !== this.appId) {
      throw "appid not match";
    }

    return decryptResult;
  } catch (error) {
    console.log("UserInfoDecrypt.decryptData() error", error);
  }
};


第三步:在小程序根目录下创建文件夹 utils,将 cryptojs-master 复制进去


微信小程序 session_id_微信小程序 session_id_03


三、获取 SessionKey

回到文章顶部的流程图,我们需要首先获得用户的 session_key。

值得注意的是:微信禁止我们在小程序中获取 session_key,并且要求我们在自己的服务器端进行获取。

所以我在这里使用了小程序的云函数功能进行开发。

(如果您还不会使用云函数,请先阅读下面这篇文章)

邵励治:MiniProgram|极简微信小程序「云函数」入门(30s读完)zhuanlan.zhihu.com

微信小程序 session_id_微信小程序 session_id_04


第一步:创建云函数 getSessionKey 并安装 axios 在云函数上

如何创建云函数请看上面的「邵励治:MiniProgram|极简微信小程序云函数入门」

(下面描述下如何安装 axios)

右键云函数文件夹,点击「在终端中打开」,npm 安装 axios


npm install axios


第二步:定义云函数 getSessionKey:请求 code2Session 接口

auth.code2Session | 微信开放文档developers.weixin.qq.com

在你的云函数中写如下代码,请替换 APP_SECRET 为您自己的


// 云函数入口文件
const cloud = require("wx-server-sdk");
const axios = require("axios");
const APP_SECRET = "小程序 AppSecret";

cloud.init();

// 云函数入口函数
exports.main = async (event, context) => {
  return new Promise(async (resolve, reject) => {
    try {
      // 传入数据检测
      if (!event.code) {
        reject("input error: code");
      }

      // 获取传入数据
      const wxContext = cloud.getWXContext();
      const code = event.code;

      // 使用 axios 请求 session_key
      const result = await axios({
        method: "get",
        url: `https://api.weixin.qq.com/sns/jscode2session?appid=${wxContext.APPID}&secret=${APP_SECRET}&js_code=${code}&grant_type=authorization_code`
      });

      // 返回数据
      console.log("getSessionToken success", result.data.session_key);
      resolve(result.data.session_key);
    } catch (error) {
      console.log("getSessionToken error", error);
      reject(error);
    }
  });
};


第三步:在小程序中调用云函数 getSessionKey

根据 SoC 即关注点分离法则,我们需要在两个文件中编写小程序代码:

  • Api.js:数据层
  • index.js:小程序某个 Page 的 JS 文件

Api.js 用于访问云函数:


// 微信登录的封装
function wxLogin() {
  return new Promise(async (resolve, reject) => {
    try {
      wx.login({
        success(res) {
          if (res.code) {
            //发起网络请求
            resolve(res.code);
            console.log("wxLogin success", res.code);
          } else {
            throw res.errMsg;
          }
        }
      });
    } catch (error) {
      console.log("wxLogin error", res.errMsg);
      reject(error);
    }
  });
}

// 微信云函数:getSessionKey
function getSessionKey(code) {
  return new Promise(async (resolve, reject) => {
    try {
      const result = await wx.cloud.callFunction({
        name: "getSessionKey",
        data: {
          code
        }
      });
      console.log("getSessionKey success", result.result);
      resolve(result.result);
    } catch (error) {
      console.log("getSessionKey error", error);
      reject(error);
    }
  });
}
module.exports = {
  wxLogin: wxLogin,
  getSessionKey: getSessionKey
};


index.js 是小程序某个 Page 的 JS 文件:


import Api from "../../utils/api.js";

Page({
  async onLoad() {
    try {
      const code = await Api.wxLogin();
      const sessionKey = await Api.getSessionKey(code);
    } catch (error) {}
  }
});


此时我们就可以获得 sessionKey 了!

四、获取 unionid

我们知道,unionid 需要如下几个参数:

  • APPID:事先准备
  • session key:在第三步获取到了
  • encryptedData:需要使用 wx.getUserInfo 获取
  • iv:需要使用 wx.getUserInfo 获取

第一步:API:查看是否拥有 wx.getUserInfo 调用权限


// 查询是否有权限使用 wx.getUserInfo
function hasAuthGetUserInfo() {
  return new Promise((resolve, reject) => {
    try {
      wx.getSetting({
        success(res) {
          let result;
          if (res.authSetting["scope.userInfo"]) {
            result = true;
          } else {
            result = false;
          }
          console.log("hasAuthGetUserInfo success", result);
          resolve(result);
        }
      });
    } catch (error) {
      console.log("hasAuthGetUserInfo error", error);
      reject(error);
    }
  });
}


第二步:API:获取 UserInfo


// 获取 UserInfo
function getUserInfo() {
  return new Promise((resolve, reject) => {
    try {
      wx.getUserInfo({
        success: function(res) {
          console.log("getUserInfo success", res);
          resolve(res);
        }
      });
    } catch (error) {
      console.log("getUserInfo error", error);
      reject(error);
    }
  });
}


第三步:API:获取 unionid


// 获取 unionid
function getUnionid(appId, sessionKey, encryptedData, iv) {
  return new Promise((resolve, reject) => {
    try {
      const userInfoDecrypt = new UserInfoDecrypt(appId, sessionKey);
      const data = userInfoDecrypt.decryptData(encryptedData, iv);
      console.log("getUnionid success", data.unionId);
      resolve(data.unionId);
    } catch (error) {
      console.log("getUnionid error", error);
      reject(error);
    }
  });
}


第四步:在小程序里面使用 API 获取 unionid


async onLoad() {
    // 判断是否应该显示登录框
    if (!(await Api.hasAuthGetUserInfo())) {
      this.showLoginDialog();// 这个是我自己写的弹出 dialog 以便用户进行登录的方法,大家不用管它
      return;
    }

    try {
      // 微信登录
      const code = await Api.wxLogin();
      const sessionKey = await Api.getSessionKey(code);
      const userInfo = await Api.getUserInfo();
      const unionid = await Api.getUnionid(
        "wx3cf0f346f6080c78",
        sessionKey,
        userInfo.encryptedData,
        userInfo.iv
      );
    } catch (error) {}
  }


四、结语

如果你做了这一切,还是在返回数据中没有获得 unionid。

那是因为你的小程序没有绑定微信开放平台。

只有绑定了才会有 unionid。