本文试着整理微信小程序(本文简称wxApp)使用微信(weixin)登录认证和授权获取微信开放信息(OpenData)时常见的问题,如果有理解不到位的地方请留言指出。
授权
wxApp需要获取weixin提供的OpenData前需要用户授权weixin提供相关信息。这需要调用小程序提供的API wx.authorize()来向用户请求授权。但并不是所有小程序提供的API都需要用户授权才可以使用,比如说系统设备信息等其他api。用户可以提供如下授权(此授权列表以后或许会更多)。(https://mp.weixin.qq.com/debug/wxadoc/dev/api/authorize-index.html)
scope | 对应接口 | 描述 |
---|---|---|
scope.userInfo | wx.getUserInfo | 用户信息 |
scope.userLocation | wx.getLocation, wx.chooseLocation | 地理位置 |
scope.address | wx.chooseAddress | 通讯地址 |
scope.invoiceTitle | wx.chooseInvoiceTitle | 发票抬头 |
scope.werun | wx.getWeRunData | 微信运动步数 |
scope.record | wx.startRecord | 录音功能 |
scope.writePhotosAlbum | wx.saveImageToPhotosAlbum, wx.saveVideoToPhotosAlbum | 保存到相册 |
scope.camera | 摄像头 |
如果用户没有给予相应的权限,则调用对应的api时失败。可以在对应的fail回调函数中设置提醒。
wx.openSetting()
调起客户端小程序设置界面,返回用户设置的操作结果。用户打开设置界面后可以设置权限。
wx.getSetting()
获取用户的当前设置。注:返回值中只会出现小程序已经向用户请求过的权限。只获取权限不打开设置界面。
获取用户信息
wx.getUserInfo()可以获取用户信息。如果小程序希望围绕着用户提供更多增值服务,拿就需要构建自己的用户系统,每个用户都有自己的userId. 通常我们可以使用oAuth来依赖微信等第三方平台提供的用户系统来构建自己的用户系统,免去了注册等麻烦。
wx.getUserInfo()可以获取用户信息,但并不是所有的信息,毕竟这是微信非常核心的资源,他开放了一些基本信息,以及加密了一些敏感信息。
wx.getUserInfo()参考官方文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/open.html#wxgetuserinfoobject
success返回参数说明:
参数 | 类型 | 说明 |
---|---|---|
userInfo | OBJECT | 用户信息对象,不包含 openid 等敏感信息 |
rawData | String | 不包括敏感信息的原始数据字符串,用于计算签名。 |
signature | String | 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,参考文档 signature。 |
encryptedData | String | 包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法 |
iv | String | 加密算法的初始向量,详细见加密数据解密算法 |
userInfo参数说明:
参数 | 类型 | 说明 |
---|---|---|
nickName | String | 用户昵称 |
avatarUrl | String | 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像),用户没有头像时该项为空。若用户更换头像,原有头像URL将失效。 |
gender | String | 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知 |
city | String | 用户所在城市 |
province | String | 用户所在省份 |
country | String | 用户所在国家 |
language | String | 用户的语言,简体中文为zh_CN |
encryptedData 解密后为以下 json 结构,详见加密数据解密算法。主要多了openId和unionId.这是敏感信息,我们并不将其缓存在小程序端。而是存放在我们的开发者服务器端管理。
{ "openId": "OPENID", "nickName": "NICKNAME", "gender": GENDER, "city": "CITY", "province": "PROVINCE", "country": "COUNTRY", "avatarUrl": "AVATARURL", "unionId": "UNIONID", "watermark": { "appid":"APPID", "timestamp":TIMESTAMP } }
用户基本信息并不能标示用户的唯一性,为了保证其唯一性,需要使用openId和unionId. 注意openid不是userid,但openid与unionId可以用来构建自己的用户体系。openid 就如同一个房间有几个门,用户可以从任一个门进入房间,但是openid就是每个门口的守卫者验证用户身份,验证通过后才允许进入房间,并给一个标签(openid)告知是哪个用户从哪个门由谁验证过。所以同一个人从不同的房门进入时openid应该是不一样的。如果这些守卫者都是使用同一个名单登记系统来验证用户身份的,这套系统可以提供相同的unionId。
最终用户(End User):想要向某个网站表明身份的人。 标识(Identifier):最终用户用以标识其身份的URL或XRI。 身份提供者(Identity Provider, IdP):提供OpenID URL或XRI注册和验证服务的服务提供者。 依赖方(Relying Party, RP):想要对最终用户的标识进行验证的网站。
UnionID机制说明 如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
由于encryptedData是加密数据,需要解密,解密是需要code以及相关算法来实现。为了获取code,首先需要wx.login()获取。这些信息就是下面认证部分的知识点。
认证(小程序登录)
小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。
用户授权调用相应的api后并非所有的信息都能获取,对于敏感的信息需要用户认证后获取临时登录凭证code后,再次发起请求才能获取敏感信息。开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。只有拿到了openid后我们的后台程序才可以表示不同的用户,进一步围绕用户实现更多的功能。
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
wxApp可以提供JSCODE,即wx.login获取的code,APPID和SECRET也是wxApp开发者拥有的,可以直接在自己的后台服务器中设置获取。所有后台服务器可以获得openid来组建自己的用户体系。session_key是用来解密敏感数据的,针对本次session的会话密钥。这个session指的应该是weixin客户端与weixin服务器端的session,其session timeout时间据说是30天,换一个手机/硬件上使用weixin应该会有一个新的session. 而session timeout之前这个session_key应该是不变的。(如果理解有错,请帮更正。)
上面的请求将会获取到
{ openId:xxx session_key:xxx //在满足unionid条件下还会返回unionid unionid:xxxx }
在拥有了session_key后就可以对开放接口中返回的敏感数据进行解密,所有要保护好这个session_key,从而保证数据的安全。数据解密都在开发者服务器中实现,为了避免每次都去获取这个session_key,我们将其缓存起来,并生成一个服务器端3rd_session来实现映射,并把3rd_session传个小程序保存起来(wx.setStorage())用于通信。以后每次小程序向开发者服务器(server)发起请求时带上这个3rd_session(wx.getStorage()),开发者服务器(server)就可以根据映射获取session_key和openid.
小程序(wxApp)与server的3rd_session时长,我的理解应该是小于weixin与weixin server的session时长的。因为小程序是基于微信的,运行小程序就会touch微信,从而刷新session。如果3rd_session的时长设置过长大于session的时长就会有问题。但正常应该不会,因为session据说是30天(可以百度一下这个问题:微信多少天不登陆需要重新登陆?答案是一个月)。
3rd_session是存放在缓存中的,不清理的情况都能访问到。而server端的3rd_session一样是存放在缓存或数据库表中,而不是网页访问的session.因为小程序没有cookie机制。所以我们需要自己设计类session管理,而不是使用容器提供的session管理机制。
登录流程时序
官方提供的这张图比较直观,同样也比较简单,并不包括一些异常处理。实际上还有更多的事情要去做。建立自己的session管理,构建自己的用户体系,这些都是关键点。根据自己的需求去实现。
开发者服务器(Developer Service)中的用户体系
再用刚才的房间做比喻,这个房间就如我们的开发者服务器,提供各种服务;现在我们这个房间提供一个简单的服务:给每次进入房间的人发一个苹果,如果这个人之前已经拿过苹果了,就给他一个橙子;如果苹果橙子都拿过了,就什么都不给。
发苹果,发橙子本身都很容易。但问题来了,现在进来一个人,如何知道这个人是否已经拿过苹果或者橙子。ok, 很简单,查看领取记录;凡事领过的人都要记录下来。没有登记的人说明是第一次来,就发苹果。这个登记本就依赖一个简单的用户系统。把登录本上的人员抽出来就是一个简单的用户表。
对于我们开发者服务器来说提供的会员服务就需要建立一个简单的用户表。
这个用户表里只需要记录openid和unionid即可,其他用户信息可以记录也可以不记录。(因为这些信息都由微信维护着,开发者服务器是不能修改其在微信服务器中的信息)。如果记录下来的话,需要定期sync一下。