一、全局理解
全局信息流程图
问题 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
解压后的目录结构
第二步:在 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 复制进去
三、获取 SessionKey
回到文章顶部的流程图,我们需要首先获得用户的 session_key。
值得注意的是:微信禁止我们在小程序中获取 session_key,并且要求我们在自己的服务器端进行获取。
所以我在这里使用了小程序的云函数功能进行开发。
(如果您还不会使用云函数,请先阅读下面这篇文章)
邵励治:MiniProgram|极简微信小程序「云函数」入门(30s读完)zhuanlan.zhihu.com
第一步:创建云函数 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。