背景说明
写这篇文章来记录一下开发微信小程序时获取unionid踩过的坑:在解密调用接口 wx.getUserInfo返回的encryptedData后,发现只有openid,并没有unionid,不明所以。由于是第一次开发小程序,也不知道啥原因,查各种文档加上公司大佬的经验,问题得以解决,特意记录一下。
UNIONID机制说明
我们现在看一下来自微信的unionid机制说明:
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。
简而言之,unionid是用来说明多个应用下用户身份一致性的问题。相比较而言,由于在同一个应用下每个用户的openid是唯一的,因此openid也是也可以用来标记用户身份,但是仅限于在同一个应用中。对于不同的应用,每个用户的openid是不一样的,要想在不同应用间识别用户的唯一性只可以用unionid。下面来举一个例子来说明,例如:
李四开发者账号下有公众号A和小程序B,对于访问公众号A和小程序B的用户张三有openidA与openidB(openidA!=openidB),openidA在公众号A应用中是唯一的,可以说明张三的身份;openidB在小程序B中是唯一的,也可以说说明张三的身份。假如,我们现在要确定公众号A的用户张三与小程序B的用户张三是否为同一个用户张三,我们根据openid能确定吗?很显然,不可以。此时用户的unionid就可以解决这个问题,即unionid是用来确定不同应用下用户的唯一性。
UnionID获取
我们先看一下微信官方给出的途径:
绑定了开发者帐号的小程序,可以通过以下途径获取 UnionID。
- 调用接口 wx.getUserInfo,从解密数据中获取 UnionID。注意本接口需要用户授权,请开发者妥善处理用户拒绝授权后的情况。
- 如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号。开发者可以直接通过 wx.login + code2Session 获取到该用户 UnionID,无须用户再次授权。
- 如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用。开发者也可以直接通过 wx.login + code2Session 获取到该用户 UnionID ,无须用户再次授权。
- 用户在小程序(暂不支持小游戏)中支付完成后,开发者可以直接通过getPaidUnionId接口获取该用户的 UnionID,无需用户授权。注意:本接口仅在用户支付完成后的5分钟内有效,请开发者妥善处理。
- 小程序端调用云函数时,如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号,可在云函数中通过 cloud.getWXContext 获取 UnionID。
- 小程序端调用云函数时,如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用,也可在云函数中通过 cloud.getWXContext 获取 UnionID。
大多数情况下均可以通过前三种方法获取到。途径2和3说明只要你曾经关注过该公众号就可以获取到unionid,不论现在是否还关注公众号。在项目中采取下列方法来获取unionid:
- 通过 wx.login + code2Session 获取到该用户 unionid;如果获取的unionid为空则转2;
- 调用接口 wx.getUserInfo,从解密数据中获取 unionid。
为什么不直接调用wx.getUserInfo获取unionid呢?
在实际开发项目中,只要需要解密微信的敏感数据,比如:unionid,手机号等信息,就必须调用wx.login来获取加解密的会话密钥session_key,调用wx.getUserInfo获取unionid时,返回的是加密的数据,必须解密才可以拿到。调用wx.getUserInfo之前,如果已经拿到密钥,需要调用wx.checkSession来检查会话密钥是否有效,无效的话需要调用wx.login来获取新的session_key。切记,这步操作一定要在调用wx.getUserInfo之前进行!如果在调用wx.getUserInfo后在调用wx.login,会刷新session_key,后端解密时会报错:pad block corrupted,导致解密失败,因此顺序一定不能颠倒。
然而在解密wx.getUserInfo返回的encryptedData后,发现居然没有unionid,真是惊呆我!我们来看一下解密的信息:
{
"country": "",
"watermark": {
"appid": "wxappid",
"timestamp": 1611821044
},
"gender": 2,
"province": "",
"city": "",
"avatarUrl": "",
"openId": "hGFiK5FzmSFR7wkElxn_jw7EDSno",
"nickName": "hhh",
"language": "zh_CN"
}
注:以上数据不是真实数据,只是用来说明问题
我们再来看看微信文档给出的示例:
{
"openId": "OPENID",
"nickName": "NICKNAME",
"gender": GENDER,
"city": "CITY",
"province": "PROVINCE",
"country": "COUNTRY",
"avatarUrl": "AVATARURL",
"unionId": "UNIONID",
"watermark": {
"appid": "APPID",
"timestamp": TIMESTAMP
}
}
可以发现,两者的差别:前一个未获取到unionid,官方给出的示例是有unionid的。
出现以上问题的原因是:小程序未在微信开放平台绑定,只有在微信开放平台下绑定小程序才可以获得unionid,未绑定的小程序只能获取到openid。
微信开放平台链接:https://open.weixin.qq.com/,登录后绑定即可。
在微信官方文档中也给出了的操作的方法:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/union-id.html
按照提示操作即可。
在看微信开放文档的更新日志时,发现微信官方更新了wx.login API,默认返回unionid,微信的更新日志:
我们重新看一下unionid的获取方式:
- 开发者可以直接通过 wx.login +
code2Session
获取到该用户 UnionID,无须用户授权。- 小程序端调用云函数时,可在云函数中通过 cloud.getWXContext 获取 UnionID。
相比较原来获取unionid的方式,更新后的方式更加简单,只需要通过wx.login+code2Session就可以获取到用户的unionid。不过要想获取到unionid,必须绑定到微信开放平台,不绑定是获取不到unionid的。
2021年4月25日更新