实战前端微信小程序登录体系设计

638 阅读4分钟

实战前端微信小程序登录体系设计

🍎 微信官方提供了两种标识:

  • OpenId 是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户。
  • UnionId 是一个用户对于同主体微信小程序/公众号/APP 的标识,开发者需要在微信开放平台下绑定相同账号的主体。开发者可通过UnionId,实现多个小程序、公众号、甚至 APP 之间的数据互通

🍎 首先区分微信小程序登录和用户手机号码授权和头像昵称授权的问题

wx.login() 官方地址 login

  • 我们在实现登录的时候,其实调用wx.login就可以实现登录功能,就能勾标识一个用户,就能够获取到openid、unionid、session_key等信息。获取手机号和头像昵称都是各个开发商为了自己业务信息的完整,或者业务流程使用了手机号码和用头像昵称来服务用户。所以很多时候我们会误以为登录和头像手机号等信息是一体的,早期微信的设计就有很多小程序在wx.login的时候弹起授权微信昵称的弹窗,用户点了拒绝,如果不进行相应的处理就会使用不了小程序。同时也会导致很多用户放弃使用小程序。所以现在获取手机号码和头像昵称它们是分开的。

  • session_key 有过期失效,需要服务端就行数据库存储,不因该频繁的调用。在登录失效检查的时候如果登录过期,重新调用登录接口,替换掉原来的 session_key。

实现步奏

1、 登录实现

const silentLogin = () => {
  return new Promise((resolve, reject)=>{
    wx.login({
      success: loginCode=> {
        let data = {	code: loginCode.code	}
        request('登录接口', data).then(res=>{
          let { userInfo} = res.values;
          app.globalData = Object.assign(app.globalData, userInfo) // 把登录信息存放在全局后续使用
          resolve();
        }).catch((err) => {
          reject(err);
       })
      },
     fail:(err)=>{
        reject(err);
      }
    })
  })
}

2、检查用户登录(获取openid), 我们一般都是在page页面的onLoad生命周期里先调用这个函数来获取登录状态。当然在不需要登录来调用的借口是不用调用的。

const getOpenid = (check = false)=>{
	// 是否开启强制检查 check
  let that = getCurrentPages().pop();
  return new Promise(function (resolve, reject) {
    if(app.globalData.openid){
      if(check){
        wx.checkSession({
          success:()=>{
            resolve();
          },
          fail:()=>{
            login().then(loginRes=>{
              resolve();
            });
          }
        })
      }else{
        resolve();
      }
    }else{
      login().then(loginRes=>{
        resolve();
      },err=>{
        reject(err);
      });
    }
  }).then(()=>{
    getPhoneAndNike(that); 
  })
}


3、 getPhoneAndNike 是我们用户获取该用户时候授权了手机号和头像昵称,用户后续在页面中判断是否需要授权头像昵称。

const getPhoneAndNike = (that = getCurrentPages().pop())=>{
  that.setData({
    isLogin: !!app.globalData.phoneNumber,
    hasNick: !!(app.globalData.nickName && app.globalData.avatar)
  })
}

4、授权手机号

代码示例

<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
getPhoneNumber (e) {
    console.log(e.detail.errMsg)
    console.log(e.detail.iv)
    console.log(e.detail.encryptedData)
    loginByPhone(e.detail.encryptedData, e.detail.iv)
  }
  • encryptedData 包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法
  • iv 加密算法的初始向量,详细见加密数据解密算法
  • cloudID 敏感数据对应的云 ID,开通云开发的小程序才会返回,可通过云调用直接获取开放数据,详细见云调用直接获取开放数据

以上数据需要服务端进行解密

const loginByPhone = ({encryptedData, iv, type = ''})=>{
  return new Promise((resolve, reject)=>{
    // 解密前检查登录是否失效,不重复效用登录接口,防止频繁更新session_key
    wx.checkSession({
      success:()=>{
        // 调用解密接口获取解密后的手机号
        return updateUserInfo({encryptedData, iv}).then(res=>{
          resolve();
        }, err=>{
          reject();
        });
      },
      fail:(err)=>{
        // 登录失效就先登录
        login()
        reject(err); 
      }
    })
  })
}



const updateUserInfo = ({encryptedData, iv})=>{
  let that = getCurrentPages().pop();
  return new Promise((resolve, reject)=>{
    let data = {
      openid: app.globalData.openid,
      encryptedData: encryptedData,
      iv: iv
    }
    request('服务端解密接口', data).then(res=>{
      let { userInfo } = res.values;
      app.globalData = Object.assign(app.globalData, userInfo);
      getPhoneAndNike(that);
      resolve();
    }).catch(err=>{
      reject(err)
   })
  })
}

5、获取头像昵称

目前已经进行了调整老版本的wx.getUserInfo已经废弃。 详细的调整说明

const getUserProfile = ({type = '', desc = '给用户更好的服务'})=>{
  return new Promise((resolve, reject)=>{
    if(!!(app.globalData.nickName && app.globalData.avatar)){
      getPhoneAndNike();
      resolve()
    }else{
      wx.getUserProfile({
        desc,
        success:(info)=>{
          app.globalData = Object.assign(app.globalData, { avatar: info.userInfo.avatarUrl, nickName: info.userInfo.nickName});
          getPhoneAndNike();
         // 更新服务器存的邮箱昵称
          updateProfile({...info.userInfo, openid: app.globalData.openid})
          resolve(info)
        },
        fail:(err)=>{
          reject(err)
        }
      })
    }
  })
}


const updateProfile = (info)=>{
  getPhoneAndNike();
  return new Promise((resolve, reject)=>{
    let data = {
      info: info,
    }
    request('服务器地址', data).then(res=>{
      resolve(res);
    },err=>{
      reject(err);
    })
  })
}

总结: 总的登录流程大概就是进入小程序时静默登录,检查登录状态是否失效,失效重新获取登录信息。获取头像昵称的时候需要检查一下登录状态,然后获取手机号时需要解密。同时服务端更新用户的手机号和头像昵称就可以。