微信的静默授权机制给小程序开发者提供了很大的方便,使得在用户无需使用账号密码体系登录的前提下,能够获取用户的身份。但是很多人只是知道当用户访问小程序时,开发者能够获取用户的 OpenID ,这个参数可以作为用户身份的唯一识别码,却很少关心其背后的机制,本文就来讲讲,从用户打开小程序开始,到获取用户的 OpenID 的过程中,发生了什么,以及微信服务端是如何处理这里的整个逻辑的。
流程概述
以开发者的角度,整个流程为:
大致的步骤是:
- 小程序调用“获取登录凭证”接口,获取由微信返回的 code 。
- 小程序端将 code 传送给开发者服务端,由服务端将获取的 code 以及 AppID 和 AppSecret ,发送给微信服务端。
- 开发者服务端获取微信服务端返回的 OpenID 和会话密钥等信息。
图上后续的流程为获取 OpenID 后以及之后开发者服务端与小程序端和微信服务端交互的流程,不在本文叙述范围内。
获取登录凭证
用户打开小程序后,小程序端调用 wx.login() 方法,之后直接获取返回的 code (用户登录凭证),我们来看看,这个过程中,微信做了什么。
这个过程中需要的一张数据表我们标记为“用户登录凭证表”。
备注说明:事实上,存储该类信息的数据库大概率使用的是NoSQL数据库,在本文叙述中,将不再区分NoSQL数据库和关系型数据库,仅认为两者只是存储方式的不同,统一以关系型数据库的“表”、“记录”、“字段”的概念进行描述。
当小程序端调用 wx.login() 方法后,微信App携带以下信息向微信服务端发送请求:
- wx_id:微信用户的全局唯一识别码
- AppID:小程序的唯一识别码
微信服务端获取请求并识别请求为有效请求之后,向“用户登录凭证表”新增一条记录,相应的有效字段及内容分别是:
- AppID:记录发送请求的小程序。
- wx_id:记录访问者用户。
- code:即用户登录凭证,由特定规则生成即可,只需保证在有效时间内同一AppID下不重复即可。
- 是否使用:创建记录时默认为“否”,后续该 code 被使用后,变更为“是”。
完成以上步骤后,微信服务端向客户端返回 code ,以下是微信返回内容的一个示例:
到达这一步,小程序端已获取 code (用户登录凭证)。
发送登录凭证
发送 code (用户登录凭证)的过程分为2步:
- 第1步:小程序端向开发者服务端发送。
- 第2步:开发者服务端向微信服务端发送。
在这个过程中,很多人会有疑问:开发者服务端实际上充当了一个中转站的用途,为什么不改成由小程序端直接向微信服务端发送?
这里的原因,笔者分析主要有2个。一是发送 code 的过程中,需要同时携带小程序对应的 AppID 和 AppSecret 以便验证请求者的身份,这些敏感信息在前端存储极易泄漏。二是使用 OpenID 的过程,都在开发者服务端,即便由小程序端获取,最终还是需要由小程序端发送给开发者服务端。
笔者也自己做了一个测试,尝试从小程序端直接发送请求,发现微信已经从流程上杜绝了此类操作。
第一步,从小程序端向微信服务端发起请求。提示内容为:
即要请求的 https://api.weixin.qq.com 域名不在后台配置的合法域名中,后面考虑如果把这个域名加入合法域名会怎么样?
第二步,登录小程序管理后台,添加该域名,提示内容为:
即该域名不允许被加入到合法域名中。对此微信官方文档也给出了详细解释(对应上述提到的原因一):
因此,指定的这两步只能按照流程进行,没有办法绕过。
返回OpenID
在微信服务端收到由开发者服务端发送的 code 及相关信息后,将会向开发者服务端返回 OpenID 、 session_key 和 UnionID 等信息,我们这里仅说明 OpenID ,这是一个涉及判断条件较多的环节。
第一步:判断 code 有效性
只有同时满足以下条件,才认为这个 code 是有效的:
- code 在有效期内
- code 与 AppID 对应
- AppSecret 与 AppID 对应
前两者只需将请求的内容与“用户登录凭证表”进行比较即可得出,第3项需查询对应的小程序账户信息表。
测试中发现过期的 code 和无效的 code 给出的提示都是 invalid code ,猜测可能对于 code 存在定时清空机制,对于超时的 code 直接做了删除处理。在判断达到以上条件后,就进入第2步。
第二步:生成OpenID
每一个用户的 OpenID 需要在指定的小程序下保持全局唯一,而生成的 OpenID 是定长的(指长度在指定字符数内),那么无论按照任何算法,都必然存在“可能出现2个用户初次生成的OpenID重复”的情况,所以对于OpenID信息,最好的策略不是实时生成,而是采用以下策略:
按照规则生成后,判断该小程序下是否重复,如不重复,则存表,如重复,则加干扰项,重新生成,直到不重复为止。
微信实际采取的策略不得而知,但无非是以上规则的加强版。一个最为简单的规则就是,将用户的 wx_id 和 AppID 直接转换成数字,然后将相乘的结果再转换成字符串。如重复,则将结果数字加1即可。
对于一个有效的 code ,根据筛选 wx_id 和 AppID ,有记录则直接返回该值,如果无,则按照规则生成。
第三步:返回OpenID
生成 OpenID 之后,就由微信服务端向开发者服务端返回结果。
至此,开发者服务端获取访问者的 OpenID 信息,即完成“静默登录”动作,这一套流程的优点是,在小程序上,无需用户输入账号密码,即可完成登录。在完成登录之后,后续的小程序端和开发者服务端之间的交互可直接依照常规的完成登录的方式进行,例如向小程序端发送 access_token 等,以此作为身份凭证。
总结
微信小程序的这套“静默登录”机制给开发者提供了很大的方便,也方便了用户的操作,开发者在设计产品时,要充分利用这套“静默登录”机制,除非是存在多类型客户端情况,否则就没有必要让用户进行相应的注册登录操作,在这一点上一定要改变思维。