授权 vs 登录
在这篇文章里我仔细讲了小程序登录相关知识。那么为什么用户登录之后还需要授权给开发者呢?登录其实相当于告诉开发者一个用户的唯一ID,无论什么时候用户进入小程序,小程序都能通过这个唯一ID认出用户来。但是,有的时候,开发者可能不单单需要认出这个用户来,还需要知道这个用户的一些其他信息。比如,做一个商城小程序,开发者可能需要知道这个用户的手机号,以便于在订单发货之后短信通知用户。为了获取用户的一些额外属性信息,就需要用户授权。微信对用户的隐私保护得比较好,开发者想获取用户的信息,微信都会通过授权弹窗的方式让用户知晓,只有用户知晓并同意来,开发者才能获取到用户的ID以外信息。
微信里授权的方式比较
微信里常用的几种授权方式是:
- 直接调用具体API唤起弹窗授权:对于需要授权才能调用的API,如果开发者直接调用调用这个API,则微信会先弹出一个授权弹窗,让用户确认,用户授权之后才会返回结果,否则这个调用就会失败。
- 通过wx.authorize提前授权:通过wx.authorize这个API提前询问授权,无论用户允许不允许都将结果记录下来,之后调用需要这个授权的API时,不需要用户再次确认。
- 点击按钮唤起授权:对于用户敏感信息、用户手机号等信息,不能由开发者主动唤起弹窗,而是必须由用户主动点击按钮授权,而且敏感信息还需要配合后端进行对称加解密,方能拿到数据;如果用户已拒绝,再次点击按钮,仍然会弹窗。
对于前两种授权方式,其实非常相似:用户授权一次,除非**用户手动删除小程序,**否则这个授权信息都会保存下来(无论拒绝或者允许),后续调用所需权限的API时都不会再次弹窗。 前两种授权方式的不同点:第二种方式能手动控制第一次弹窗的时机,而第一种弹窗的时机只能是调用具体API的时机。(这里其实埋了个坑,即开发者可以在小程序启动阶段就弹出授权,给用户造成不好的体验。) 前两种授权方式跟第三种有着非常大的区别:前两种授权是开发者唤起授权,第三种授权是用户主动点击按钮之后弹起授权;前两种授权信息会保存下来(用户手动删除小程序时清空),以保证后面不再弹窗授权,第三种授权是用户每次点击都能重新授权。前两种授权获取到的信息是明文,第三种授权获取到的信息需要配合服务端解密才能使用。
敏感信息的解密
对于获取用户敏感信息的API,如手机号,小程序客户端获取到的是加密之后的手机号。小程序客户端需要将加密数据传给小程序服务端,服务端拿着用户登录时获取到的session_key去解密,才能获取到解密之后的信息。 session_key具有以下这些特点:
- wx.login调用时,用户的 session_key可能会被更新而致使旧 session_key 失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用wx.login,并非每次调用都导致 session_key 刷新)。开发者应该在明确需要重新登录时才调用wx.login,及时通过auth.code2Session接口更新服务器存储的 session_key。
- 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key 进行续期。用户越频繁使用小程序,session_key 有效期越长。
- 开发者在 session_key 失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口wx.checkSession可以校验 session_key 是否有效,从而避免小程序反复执行登录流程。
- 当开发者在实现自定义登录态时,可以考虑以 session_key 有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
根据以上说明,我们可以看出解密的流程应该是:
一个常见的坑
以上的方案看着很美好,客户端检测session有效性来决定是否重新登录,以此保证session_key有效,只要session_key有效,解密必然成功。然而,现实中,即使checkSession成功了,解密也偶尔会失败,具体原因还可以探讨,但是确实在微信开发者社区有着大量这样的吐槽,比如这个。于是最终实践靠谱的流程就变成了这样:
至于为什么checkSession成功了,而解密却失败了呢? 这个我有个猜想,但是目前也没有实锤的证据,在网上也没有找到靠谱的说法,如果你了解原因,欢迎跟我进一步交流。