小程序授权登录最佳实践
in 默认分类 with 0 comment

小程序授权登录最佳实践

in 默认分类 with 0 comment

先扔小程序

1111.jpg

前言

2019 年 9 月 1 号开始,小程序内账户登录系统执行新的规定 具体可看官方文档:微信小程序整改

这个整改大致上说的是必须让用户进入小程序体验后,在使用某些具体需要识别用户的功能的时候才能登录,不允许一进入小程序就强制登录,禁止不登陆就不给用的逻辑出现,因为这对用户来说体验非常差。

最近利用业余时间做了个小程序,正好记录下登录流程。

官方登录流程时序

api-login.2fcc9f35.jpg

大致说下这个流程:小程序端有个api是wx.login(Object object),这个接口不需要用授权,可以拿到一个code。拿到后将code传到后端,让后端调用微信的auth.code2Session接口,拿到openidsession_key。这个openid就是用户的唯一标识。

其实到这一步就算是登陆成功了,因为已经可以标记用户,可以自己建立数据库维护这个openid,每次跑完上述的流程拿到openid,再去数据库匹配拿到用户的相关数据。

但是切记不要直接把这个openid返回给前端,因为用户对于小程序来说,openid是不变的,泄露了就容易伪造请求。所以需要生成一个有时间限制的临时凭证token,每次访问需要识别用户身份的接口都利用这个token来识别,token可以限制个时长几小时,防止泄露后无限使用。

获取用户信息

其实小程序中需要用户授权的是这一步。

小程序有个叫wx.getUserInfo(Object object)的API,这个API可以获取用户的公开信息,例如昵称、头像、性别、所在城市等。但是调用这接口,需要在用户授权的情况下,才能通过这个接口获取到用户信息。在很久很久以前,调用这个wx.getUserInfo(Object object)就会拉起授权提示,也就是这个:
1111111.png

但是后面微信修改了这个API的访问规则,为了不打扰用户,只能在用户点击button的时候触发授权提示,也就是得这么写:

<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">授权登录</button>

规则是必须为button组件,而且必须指定参数open-type="getUserInfo"bindgetuserinfo="bindGetUserInfo"是用户点击按钮后的回调函数。用户点击这个按钮,如果处于未授权状态,则会拉起上面提到的授权提示;如果处于授权状态,则不会拉起。这里需要注意的是无论是否拉起,拉起后无论用户是否授权,都会调用上面设置的bindGetUserInfo函数,这一步可以给拒绝授权的用户一个友好提示。

若用户点击授权,在这一步就可以获取到用户的公开信息了,这个信息会在bindGetUserInfo回调函数中作为参数传入。返回的参数如下:
getUserInfo接口回调

wx.getUserInfo(Object object)这个函数的作用实际上跟上面的button类似,但是在用户没授权的状态下,调用这个接口是不会拉起授权的,只有在授权状态下能返回用户信息。简而言之,只能用button的方式进行授权,若用户处在授权状态下可以随时调用wx.getUserInfo(Object object)获取用户信息。

因为没用过云开发,所以cloudID参数不做说明。

然后需要把这些参数传到后端,官方建议后端校验,如果还想拿openidunionid这种敏感数据的话,则需要解密。

用户信息校验与加密数据解密算法

校验是为了防止有人伪造信息(虽然可以不校验,但还是建议检查下),解密是为了保护敏感数据,这里不赘述具体的方法了,上面给的官方文档已经很详细了。

这里值得一提的是,在用户信息校验的时候需要用到session_key,这个参数是通过上面的wx.login接口获取到的,虽然这个session_key具有一定的时效性。虽然你可以在别的地方拿到然后存起来,但还是建议在用户信息校验与解密数据的时候,重新获取一下,这样可以省下维护这个session_key的麻烦。具体代码类似如下:

bindGetUserInfo(res) {
    var that = this;
    if (res.detail.errMsg == "getUserInfo:ok") {
      // 用户同意授权
      var encryptedData = res.detail.encryptedData;
      var iv = res.detail.iv;
      var rawData = res.detail.rawData;
      var signature = res.detail.signature;
      wx.login({
        success(res) {  
          if (res.code) {
            //发起网络请求
            wx.request({
              url: '后端URL接口',
              method: "post",
              data: {
                code: res.code,
                encryptedData: encryptedData,
                iv: iv,
                rawData: rawData,
                signature: signature
              },
              success(res) {
                // 授权完成,后端应该返回临时token,前端将用户信息存入全局变量
              }
            })
          } else {
            console.log('登录失败!' + res.errMsg)
          }
        }
      })
    }
  }

官方推荐是校验用户信息,然后解密数据获取openidunionid,然后再绑定入库。但是如果你不需要获取unionid的话,在校验用户信息通过后,用wx.login获得的code去换取openid,然后将用户信息和openid关联起来即可,不需要再去解密数据(虽然网上一堆写好的解密算法但是懒啊,逃...)

登陆方式

一般分三种做法:
一次性授权
每次进入都让用户点击登录按钮,处于授权状态下不会弹出授权提示框,会直接触发授权成功的回调
永久授权
授权一次后,将用户数据存入数据库,之后进入小程序只需要调用wx.login()接口解析出openid,再去数据库用openid拿到用户信息返回给前端
不授权
只用wx.login()获取到openid接口标识用户,缺点就是拿不到用户信息

建议使用永久授权的方法,

Responses