💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 3.4 微信登录 微信登录的整个过程,如图4-22所示。 ![微信登录的整个过程](../../images/3_1532854760668.JPG) :-: 图4-22 微信登录的整个过程 ### 3.4.1 获取微信登录凭证code wx.login是生成一个带有时效性的凭证,就像是一个会过期的临时身份证一样,在wx.login调用时,会先在微信后台生成一张临时的身份证,其有效时间仅为5分钟 ` ??.hy[界面图] `。然后把这个临时身份证返回给小程序方,这个临时的身份证我们把它称为微信登录凭证code。 由于这个临时身份证5分钟后会过期,如果黑客要冒充一个用户的话,那他就必须在5分钟内穷举所有的身份证id,然后去开发者服务器换取真实的用户身份。显然,黑客要付出非常大的成本才能获取到一个用户信息,同时,开发者服务器也可以通过一些技术手段检测到5分钟内频繁从某个ip发送过来的登录请求,从而拒绝掉这些请求。 ### 3.4.2 发送code到开发者服务器 在wx.login的success回调中拿到微信登录凭证,紧接着会通过wx.request把code传到开发者服务器,为了后续可以换取微信用户身份id(注意id不是指微信用户的微信号/密码等隐私信息,可以理解为id是微信用户在服务器的一个编号)。如果当前微信用户还没有绑定当前小程序业务的用户身份,那在这次请求应该顺便把用户输入的帐号密码(是用户在业务侧的帐号密码,而不是其微信的帐号密码)一起传到后台,然后开发者服务器就可以校验账号密码之后再和微信用户id进行绑定,小程序端的示例代码如下所示。 代码清单4-12 wx.login获取code后 ~~~ Page({ tapLogin: function() { wx.login({ success: function(res) { if (res.code) { wx.request({ url: 'https://test.com/login', data: { username: 'zhangsan', // 用户输入的账号 password: 'pwd123456', // 用户输入的密码 code: res.code }, success: function(res) { // 登录成功 if (res.statusCode === 200) { console.log(res.data.sessionId)// 服务器回包内容 } } }) } else { console.log('获取用户登录态失败!' + res.errMsg) } } }); } }) ~~~ ### 3.4.3 到微信服务器换取微信用户身份id 到了这一步,开发者的后台就拿到了前边wx.login()所生成的微信登录凭证code,此时就可以拿这个code到微信服务器换取微信用户身份。 微信服务器为了确保拿code过来换取身份信息的人就是刚刚对应的小程序开发者,到微信服务器的请求要同时带上AppId和AppSecret,这两个信息在小程序管理平台的开发设置界面(小程序管理平台地址`https://mp.weixin.qq.com`)可以看到。由此可以看出,AppId和AppSecret是微信鉴别开发者身份的重要信息: `AppId`是公开信息,泄露AppId不会带来安全风险,但是`AppSecret`是开发者的隐私数据不应该泄露,如果发现泄露需要到小程序管理平台进行重置AppSecret,而code在成功换取一次信息之后也会立即失效,即便凭证code生成时间还没过期。 开发者服务器和微信服务器通信也是通过HTTPS协议,微信服务器提供的接口地址是: `https://api.weixin.qq.com/sns/jscode2session?appid=<AppId>&secret=<AppSecret>&js_code=<code>&grant_type=authorization_code` URL的query部分的参数中 \<AppId>, \<AppSecret>, \<code> 就是前文所提到的三个信息,请求参数合法的话,接口会返回以下字段。 :-: 表4-3 jscode2session接口返回字段 | 字段 | 描述 | | --- | --- | | openid | 微信用户的唯一标识 | | session_key | 会话密钥 | | unionid | 用户在微信开放平台的唯一标识符。本字段在满足一定条件的情况下才返回。| `openid`就是前文一直提到的微信用户id,可以用这个id来区分不同的微信用户。 `session_key`则是微信服务器给开发者服务器颁发的身份凭证,开发者可以用session_key请求微信服务器其他接口来获取一些其他信息,由此可以看到,session_key不应该泄露或者下发到小程序前端。 设计session_key的原因:如果我们每次都通过小程序前端wx.login()生成微信登录凭证code去微信服务器请求信息,步骤太多造成整体耗时比较严重,因此对于一个比较可信的服务端,给开发者服务器颁发一个时效性更长的会话密钥就显得很有必要了。session_key也存在过期时间,具体内容可以参考小程序的官方文档关于session_key的相关介绍。 ### 3.4.4 绑定微信用户身份id和业务用户身份 在3.4.2节提到,业务侧用户还没绑定微信侧身份时,会让用户填写业务侧的用户名密码,这两个值会和微信登录凭证一起请求开发者服务器的登录接口,此时开发者后台通过校验用户名密码就拿到了`业务侧的用户身份id`,通过code到微信服务器获取`微信侧的用户身份openid`。微信会建议开发者把这两个信息的对应关系存起来,我们把这个对应关系称之为“绑定”。 有了这个绑定信息,小程序在下次需要用户登录的时候就可以不需要输入账号密码,因为通过wx.login()获取到code之后,可以拿到用户的微信身份openid,通过绑定信息就可以查出业务侧的用户身份id,这样静默授权的登录方式显得非常便捷。 ` ??.hy[这样静默授权的登录方式是否有风险] ` ### 3.4.5 业务登录凭证SessionId 3.4.3节已经说到`微信侧返回的session_key`是开发者服务器和微信服务器的会话密钥,同样道理,开发者服务器和开发者的小程序应该也有会话密钥,在本书中我们就把它称之为`SessionId`。 >[info] hy:使用http协议,因为http协议是无状态协议,可以前端(browser)和后端(server)通过Session会话机制免去每次通信时的3次握手,从而提高通信效率。 > session_key中,前端:开发者服务器;后端:微信服务器 > SessionId中,前端:开发者的小程序;后端:开发者服务器 用户登录成功之后,开发者服务器需要生成会话密钥SessionId,在服务端保持SessionId对应的用户身份信息,同时把SessionId返回给小程序。小程序后续发起的请求中携带上SessionId,开发者服务器就可以通过服务器端的Session信息查询到当前登录用户的身份,这样我们就不需要每次都重新获取code,省去了很多通信消耗。我们在4.6.4还会提到如何利用本地数据缓存的能力把SessionId存储起来,以便在它还没过期的时候能重复利用,以提高通信的性能。