本文所说的 userInfo 仅包括以下七项信息:用户昵称、头像、性别、国家、省份、城市、语言。openid、unionid 属于用户身份标志符,这里的 userInfo 不包括用户身份标识。
2021-10-07 更新:微信发布公告表示,从 2021 年 10 月 20 日开始,小程序和公众号获取 userInfo 时,不再返回真实的国家、省份、城市、性别、语言。
2021-12-07 更新:从 2021 年 12 月 27 日开始,关注公众号后,通过公众号的 API 无法获得用户的头像、昵称。由于 10 月 20 日开始已经无法获得 userInfo 中的国家、省份、城市、性别、语言,现在又加上头像、昵称,意味着公众号无法获取任何 userInfo 了。当然,用户身份标识还可以继续通过该 API 获取。
2022-05-24 更新:微信发布公告,微信小程序 wx.getUserProfile
和 wx.getUserInfo
两个接口将被收回,这意味着继公众号无法获取任何 userInfo 后,小程序也无法获取任何 userInfo。从 2022-10-26 开始,开发者将只能通过头像昵称填写能力让用户填写头像昵称。所以这篇文章关于获取 userInfo 的方法已基本过时(除了旧版基础库),但关于 openid、unionid 的获取和使用方法还有效。
2022-01-04 更新:从 2022 年 2 月 21 日 24 时起回收通过<open-data>
展示个人信息的能力,具体看官方公告。
首先要明确,你真的需要拿到用户的 userInfo 信息吗?如果你拿到 userInfo 只是为了展示用,可直接使用 <open-data />
组件,它可以在小程序界面展示这些信息:昵称、头像、性别、城市、语言等。
如果你的小程序在服务器保存了用户的数据,例如用户创建的投票信息,用户的备忘录,用户的视频等等,那你的小程序通常需要登陆功能,这意味这你可能需要在服务器保存一份用户的 userInfo、unionid、openid 等信息,如果是这样,你需要继续往下看。
如果你使用小程序云开发的数据库,并且需要根据用户的 openid 从中读取数据,你可以使用 {openid}
这个固定的写法表示用户的 openid,这样你可以直接在前端操作数据库,而无需通过任何其他 api 先获取用户的 openid。
db.collection("test")
.where({
publisher: "{openid}",
})
.get();
从 2018 年 4 月 30 日开始 wx.getUserInfo()
就不能调出授权弹窗,这是为了避免开发者滥用该接口,随时调出弹窗影响用户体验。只有在用户已经授权过 scope.userInfo,调用这个接口才会 success 并返回 userInfo,如果以前没有授权过或者拒绝了授权,就不仅不会弹窗,还会直接 fail。
那如何让用户授权呢?
要想获得授权,需要用户主动点击 Button 组件 <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>
,点击之后会调出弹窗,询问用户是否同意授权。但是预计从 2021 年 4 月 13 日起,这个组件也不能调出弹窗了(除了 PC 端微信,下文有解释)。和 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:wx.getUserProfile()
,这个 API 只能在点击事件函数的内部调用,所以只有用户点击了某个组件才能调出弹窗,必须由用户发起。
它和以前的 <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>
有以下区别。
wx.getUserProfile()
无论用户是否曾经同意授权,任何一次点击,都会弹窗询问授权,每一次都需要用户同意,才能返回 userInfo;wx.getUserProfile()
返回的数据只有 userInfo 字段,没有 rawData、CloudID(接下来会说)、encryptedData 等数据;需要注意,截止到 2021/12/07,PC 端微信还不支持 wx.getUserProfile()
,微信给出的解决方法是:判断当前环境是否可以使用 wx.getUserProfile()
,如果不可以,则通过 <button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>
获取,具体可参考微信官方的示例代码。
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 换成 session_key,unionId、openId 更像是它的副产品,session_key 才是它的主角。session_key 大概有三个作用。
第一个作用是用来校验 userInfo 中的 rawData 数据是否真实,虽然 userInfo 通常是通过微信 api 获得的,但是为了确保数据传输过程中没有被篡改,可以用 session_key 验证 userInfo 的真实性。
第二个作用是用来解密 敏感信息的,例如 userInfo 中的 encryptedData、wx.getShareInfo
返回的 encryptedData、微信运动返回的 encryptedData、旧版手机号获取接口返回的 encryptedData 等;
第三个作用是,依据 session_key 维护用户登录状态,session_key 有有效期,有效期由微信服务器决定,用户越频繁使用你的小程序,该用户的 session_key 有效期越长,有效期不受开发者控制。开发者可以通过 wx.checkSession()
判断 session_key 是否过期,如果过期,可以认为用户已经退出了登录,可以要求用户重新登录。之所以要关注 session_key 的有效期,主要是因为正如前面所说的,session_key 需要用来解密用户的敏感信息,为了获取最新的敏感信息,就意味着隔一段时间就要用还未过期的 session_key 去解密拿到的最新解密数据。
另一个需要维护用户登录态的理由时,小程序前端向后端请求 api,如果希望保护这个 api 避免被黑客滥用,那需要在请求时提供 token 等参数方便后端判断请求是否合法,这个 token 可以是后端基于 session_key 生成,或者基于 session_key 的有效期设定 token 的有效期。
获取 OpenID 而已,为什么微信设计得这么麻烦?这是出于安全性的考虑,code
在前端通过 wx.login()
获得,后端把 code
和 appsecret
传入 auth.code2Session
才能获得 OpenID,code
因为是在前端生成所以黑客相对容易获得,但 appsecret
因为只出现在后端,所以黑客不容易获得。
获取 userInfo 有两种情况:前端小程序获取、后端服务器获取。
前端获取的方法有两种:
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>
,在它的事件函数 getUserInfo 中获取;wx.getUserInfo()
也可以获取;后端获取的方法也有两种:
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">授权</button>
获取加密的 encryptedData,然后通过 wx.login()
+ auth.code2Session
拿到 session_key,再解密 encryptedData 获取 userInfo,文档;<button open-type="getUserInfo">授权</button>
获取 CloudID,把 CloudID 传到云函数获取 userInfo,文档;这四种方法很快就不能用了(除了 PC 端微信,详看前文),因为这四种方法拿到的 userInfo 都是匿名的。
从 4 月 23 日开始不支持从后端获取 userInfo,只能通过前端获取。我的理解是,后端需要的话,好像只能自己先把敏感数据加密了再发送给后端。
如果之前没有授权过,想获得真实 userInfo:只有一种方法,用 wx.getUserProfile()
,它每次都会弹窗让用户授权,如果不想重复询问,可以用 wx.authorize()
判断一下是否已经授权过。
如果之前已经授权过,想获得 userInfo:也只有一种方法,同上,所以在第一次获得 userInfo 的时候,应该考虑妥善保存用户的 userInfo,方便后续直接从数据库中获取 userInfo。
正如前文所述,由于 PC 端微信暂时不支持 wx.getUserProfile()
,所以 PC 端微信需要特殊处理。
openId、unionId 的获取没有授权机制,因为不需要授权,有两种获取方法:
wx.login()
+ auth.code2Session
获取,文档;cloud.getWXContext()
获取,注意本地调试云函数不会返回 unionId,文档;在以前,第一种方法需要用户关注绑定在同一个微信开放平台的公众号才能获取 unionId,现在不需要关注这个公众号。
如果你只是在小程序端使用 openid 操作数据库,那可以通过 {openid}
直接表示 openid,正如本文开头所述。
回到文章标题,不弹窗,获取完整的真实的 userInfo 不可能,而且每一次获取都要弹窗。而 unionid、openid 的获取不需要经过用户授权,不需要弹窗。
如果你的微信开放平台绑定了小程序和公众号,并且用户关注了这个公众号,那其实是可以实现不弹窗获取用户 userInfo 的:小程序端获取 unionId 不需要弹窗询问授权, 公众号利用相关的 API 可以不弹窗询问授权同时获取用户的 unionId 及 userInfo,两者对接起来,就可以实现小程序端不弹窗询问授权,也可以获取完整的 userInfo。当然,这种方法以前也能用。
2021/12/07 更新:正如前文所述,从 2021 年 12 月 27 日开始,通过公众号 API 无法获取任何 userInfo,unionId 及公众号的 openid 可以继续获取。
相关文章: