不弹窗授权, 微信小程序获取用户 userInfo、unionid、openid 的方法

作者: 科技微讯

日期:

微信 2021 年 2 月 24 日发布公告,表示将对小程序登录、用户信息相关接口进行调整,以下是我的归纳总结。

下面所说的 userInfo 一律指用户昵称、头像、性别、所在城市等信息,而 openid、unionid 属于用户身份标志符。

你真的需要 userInfo 吗?

首先要明确,你真的需要拿到用户的 userInfo 信息吗?如果你拿到 userInfo 只是为了展示用,可直接使用 <open-data /> 组件,它可以在小程序界面展示这些信息:昵称、头像、性别、城市、语言等。

如果你的小程序在服务器保存了用户的数据,例如用户创建的投票信息,用户的备忘录,用户的视频等等,那你的小程序通常需要登陆功能,这意味这你可能需要在服务器保存一份用户的 userInfo、unionid、openid 等信息,如果是这样,你需要继续往下看。

旧的接口失去了弹窗能力,也不能获取真实的 userInfo

从 2018 年 4 月 30 日开始 wx.getUserInfo() 就不能调出授权弹窗,这是为了避免开发者滥用该接口,随时调出弹窗影响用户体验。只有在用户已经授权过 scope.userInfo,调用这个接口才会 success 并返回 userInfo,如果以前没有授权过或者拒绝了授权,就不仅不会弹窗,还会直接 fail。

那如何让用户授权呢?

要想获得授权,需要用户主动点击 <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>,点击之后会调出弹窗,询问用户是否同意授权。但是预计从 2021 年 4 月 13 日起,这个组件也不能调出弹窗了。和 wx.getUserInfo() 不同的是,虽然不弹窗,但也不会因此 fail,而是直接 success 并返回 rawData、encryptedData、userInfo 等数据,但是,userInfo 不再是真实的,而是匿名的:

{
  "userInfo": {
    "avatarUrl": "一个指向灰色头像的 URL",
    "city": "",
    "country": "",
    "gender": 0,
    "language": "",
    "nickName": "微信用户",
    "province": ""
  },
  "rawData": "userInfo字段的字符串格式",
  "encryptedData": "这个数据解密之后可以获得匿名的 userInfo,以及真实的 unionid、openid",
  "cloudID": "如果开通了小程序云开发,还会返回这个数据"
}

从 4 月 13 日开始,wx.getUserInfo() 在用户已经同意授权的情况下,获得的信息和上面的 button 一样,即 userInfo 是匿名的。

总结一下,从 4 月 13 日开始,以上两种方法还可以用,但都不能弹窗,并且都不能获得真实的 userInfo,甚至连获得的 encryptedData 解密后返回的 userInfo 也是匿名的,幸好解密后获得的 unionid、openid 是真实的。

如果开通了小程序云开发,以上两种方法获得的 cloudID 也不能用来获取真实的 userInfo,只能获得匿名的 userInfo。

要想取得真实的 userInfo,需要用新 API

那怎样才能获得真实的 userInfo 呢?微信推出了一个新的 API:wx.getUserProfile(),这个 API 只能在点击事件函数的内部调用,所以只有用户点击了某个组件才能调出弹窗,必须由用户发起。

它和以前的 <button open-type="getUserInfo">授权</button> 有以下区别。

  • 以前的 button 组件,如果用户从未授权,点击之后会弹窗询问授权,但是如果用户过去已经授权过,再点击不会弹窗,而是直接向开发者返回 userInfo;
  • wx.getUserProfile() 无论用户是否曾经同意授权,任何一次点击,都会弹窗询问授权,每一次都需要用户同意,才能返回 userInfo;
  • wx.getUserProfile() 返回的数据只有 userInfo 字段,没有 rawData、CloudID(接下来会说)、encryptedData 等数据;

wx.login() 可直接获取 unionid、openid

注意获取 unionId 的前提是要把小程序绑定在微信开放平台。

2021 年 2 月 23 日起,如果小程序绑定到微信开放平台,可通过 wx.login() 获取所谓的登录凭证(code),把这个 code 发送给开发者的服务器,开发者在自己的服务器中通过微信提供的 auth.code2Session 接口,就能获得用户的 unionId、openId。

在以前,不能通过 wx.login() 和 auth.code2Session 直接获取 unionId、openId,需要用户先关注了绑定到同一个微信开放平台的公众号,才能通过上述方法获取这两个 id。把小程序绑定到微信开放平台是开发者的事情,但是否关注公众号,完全由用户决定,现在取消了用户关注公众号这一环节,大大降低了获取 unionId 的难度。

需要留意的是,code2Session 顾名思义,是把 code 换成 sessionkey,unionId、openId 更像是它的副产品,sessionkey 才是它的主角。session_key 大概有三个作用。

一个是用来验证 userInfo 的数据是否真实,虽然 userInfo 通常是通过微信 api 获得的,但是为了确保数据传输过程中没有被篡改,可以用 session_key 验证 userInfo 的真实性。

另一个作用是用来解密 encryptedData,解密之后可以获得 userInfo + unionid + openid + 其他敏感数据(例如 watermark)。如果你不想验证 userInfo 的真实性,也不想获得其他敏感数据,那可以忽略 wx.login() 返回的 session_key,只关注它返回的 unionid、openid。

第三个作用是,直接用来判断用户的登录状态,sessionkey 有有效期,有效期由微信服务器决定,不受开发者控制,开发者可以通过 wx.checkSession() 判断 sessionkey 是否过期,如果过期,可以认为用户已经退出了登录,可以要求用户重新登录。

总结

以前获取 userInfo 的方法

获取 userInfo 有两种情况:前端小程序获取、后端服务器获取。

前端获取的方法有两种:

  • <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>,在它的事件函数 getUserInfo 中获取;
  • 如果以前已经授权过,用 wx.getUserInfo() 也可以获取;

后端获取的方法也有两种:

  • 开发者自己搭建的后台服务器,用 <button open-type="getUserInfo">授权</button> 获取加密的 encryptedData,然后通过 wx.login() + auth.code2Session 拿到 session_key,再解密 encryptedData 获取 userInfo,文档
  • 开通小程序云开发,用 <button open-type="getUserInfo">授权</button> 获取 CloudID,把 CloudID 传到云函数获取 userInfo,文档

这四种方法很快就不能用了,因为这四种方法拿到的 userInfo 都是匿名的。

现在获取 userInfo 的方法

从 4 月 23 日开始不支持从后端获取 userInfo,只能通过前端获取。我的理解是,后端需要的话,好像只能自己先把敏感数据加密了再发送给后端。

如果之前没有授权过,想获得真实 userInfo:只有一种方法,用 wx.getUserProfile(),它每次都会弹窗让用户授权,如果不想重复询问,可以用 wx.authorize() 判断一下是否已经授权过。

如果之前已经授权过,想获得 userInfo:也只有一种方法,同上,所以在第一次获得 userInfo 的时候,应该考虑妥善保存用户的 userInfo。

现在获取 openId、unionId 的方法

openId、unionId 的获取没有授权机制,因为不需要授权,有两种获取方法:

  • 开发者自己搭建的后台服务器,直接通过 wx.login() + auth.code2Session 获取,文档
  • 使用小程序云开发,直接在云函数通过 cloud.getWXContext() 获取,注意本地调试云函数不会返回 unionId,文档

在以前,第一种方法需要用户关注绑定在同一个微信开放平台的公众号才能获取 unionId,现在不需要关注这个公众号。

回应标题

回到文章标题,不弹窗,获取完整的真实的 userInfo 不可能,而且每一次获取都要弹窗。而 unionid、openid 的获取不需要经过用户授权,不需要弹窗。

如果你的微信开放平台绑定了小程序和公众号,并且用户关注了这个公众号,那其实是可以实现不弹窗获取用户 userInfo 的:小程序端获取 unionId 不需要弹窗询问授权, 公众号利用相关的 API 可以不弹窗询问授权获取用户的 unionId 及 userInfo,两者对接起来,就可以实现小程序端不弹窗询问授权,也可以获取完整的 userInfo。当然,这种方法以前也能用。