微信小程序自动登录逻辑分析与实现,页面黑白名单管理(上)

一、 需求与逻辑分析

产品需求为进入小程序,实现用户的自动登录。由于已经完成了app端和h5端的产品,用户登录也加入了第三方登录,在后台区分用户是根据手机号,因此各个平台的登录最主要的是与手机号绑定。而qq,微信公众号和小程序平台决定用互通的unionId,不使用OpenId登录,最终将uniodId与对应的手机号关联。
小程序平台有两种登录方式,一种是账户密码登录,另一种是微信自动登录。


微信小程序自动登录逻辑分析与实现,页面黑白名单管理(上)_第1张图片
登录页面

微信小程序自动登录逻辑分析与实现,页面黑白名单管理(上)_第2张图片
登录页面逻辑展现

二、自动登录的切入点和冲突

将登录页作为初始页,在进入登录页时先执行自动登录,如果通过wx.login可以获取uniodId,用户也绑定了手机号,就可以成功登录,否则跳转相应的页面。
起初是决定将自动登录放进app.js的生命周期onShow里执行,保证每次进入小程序都可以执行自动登录,但是这里就有了冲突。
如果是进入了其他页面,例如登录页->a->b->c,这时候突然退出刷了下微博,小程序进入了后台状态,再进入小程序,还是c页面,但这时候app.js也会重新执行。
假定c页面是我的资料页面,接口也会审查用户登录状态,如果登录状态掉线,未登录c.js的处理逻辑是跳转登录页。app.js和c.js是异步的,这俩js都检查到了用户未登录状态,这时候就会导致跳转两次登录页。
删除掉c.js接口相关的判断显然是不合理的。那么该如何处理这种app.js和进入的页面.js的冲突呢?
两种方案:
1.app.js的onShow周期下确保初始页为登录页时,执行自动登录。其他的等待页面接口判断用户是否登录,再跳转用户登录。这种的坏处是其他页面需要增加的逻辑比较多,稍微复杂。
2.进入小程序onLaunch执行登录,其他情况自动登录根据页面黑白名单验证的判断,在需要判断登录的页面加上登录状态过滤,进入页面需要登录的就用loginCheck,点击页面某个方法判断未登录状态跳转登录的调goLogin。大型项目提前做好构建后续会省事很多(由于篇幅原因,第二种方案我会另写一篇文章:微信小程序自动登录逻辑分析与实现,页面黑白名单管理(下))。
3.将app.js和需要判断用户登录状态页面.js的异步操作改成同步,这样确保app.js执行完登陆后再处理其他页面逻辑,就不会造成重复登录的状态。

三、使用
1.如果小程序页面较少,单个页面处理逻辑也很方便,采用第一种方案:

//app.js
App({
  onLaunch: function () {
    this.firstLogin();
  },
  firstLogin: function () {
    let that = this;
    wx.login({
      success: res => {
        if (res.code) {
          ajax.request({
            url: '/api/system/wx?code=' + res.code,
            method: 'get',
            success: res => {
              if (res.statusCode == 200 && res.data.openid) {
                that.globalData.openId = res.data.openid;
                let nowRoute = ajax.getCurrentPageUrlWithArgs();
                // 判断当前页是否为入口登录页,如果是,自动登录;如果不是,等待用户在页面的操作判断接口是否需要登录
                if (nowRoute == '/pages/login/index') {
                  // 如果有unionid就自动登录
                  if (res.data.unionid) {
                    that.goLogin(res.data.unionid);
                  } else {
                    // F-1-2-3:判断当前是否是登录页,不是跳转登录页
                    // 当前页保存在缓存
                    let nowRoute = ajax.getCurrentPageUrlWithArgs();
                    wx.redirectTo({
                      url: '/pages/login/index'
                    })
                  }
                }
              }
            },
            fail: function (error) {
              console.log(error)
            }
          });
        } else {
          // 微信登录失败
          wx.showToast({
            title: '网络异常,请稍后重试',
            duration: 2000,
            icon: 'none'
          })
        }
      },
      fail: res => { },
      complete: res => { },
    })
  },
// 微信登录
  goLogin: function (unionid) {
    let that = this;
    ajax.request({
      url: '/api/user/第三方登录',
      data: {
        LoginType: 'weixin',
        unionid: unionid
      },
      method: 'post',
      success: res => {
        // unionid保存在全局,保存手机号
        that.globalData.unionid = unionid;
        if (that.unionidCallback) {
          that.unionidCallback(unionid);
        }
        if (res.statusCode == 200) {
          if (res.data.code == 0 && res.data.data.usertoken) {
            //  登录成功,保存相关信息,跳转首页
            wx.redirectTo({
              url: "/pages/index"
            })
          } else if (res.data.code == -1){
            //用户未绑定手机号,选择绑定手机或者使用账户密码登录
            wx.showModal({
              title: '提示',
              content: '该微信账户未绑定手机号,是否使用账户密码登录?',
              cancelText: '密码登录',
              confirmText:'绑定手机',
              success(res) {
                if (res.confirm) {
                  // 绑定手机号
                  wx.redirectTo({
                    url: '/pages/login/phone/index'
                  })
                } else if (res.cancel) {
                  // 使用账户密码登录
                  wx.redirectTo({
                    url: '/pages/login/index'
                  })
                }
              }
            })
          } else{
            wx.showToast({
              title: '网络异常,请稍后重试',
              duration: 10000,
              icon: 'none'
            })
          }
        }
      },
      fail: function (error) {
        console.log(error)
        wx.showToast({
          title: '登录失败,请检查网络稍后重试',
          duration: 2000,
          icon: 'none'
        })
      }
    });
  },
  globalData: {
    openId: '',
    unionid: '',
  }
})

重点贴一下无unionid跳转的中间授权页的代码,解密encryptedData用了decryption.js+crypto.js


微信小程序自动登录逻辑分析与实现,页面黑白名单管理(上)_第3张图片
中间页授权获取unionid
// decryption.js
const CryptoJS = require('./crypto-js/crypto-js.js');
// 获取用户信息解密
function decryptUserInfo(session_key, encryptedData, iv) {
  // var encryptedData = CryptoJS.enc.Base64.parse(encryptedData);
  var key = CryptoJS.enc.Base64.parse(session_key);
  var iv = CryptoJS.enc.Base64.parse(iv);
  try {
    // 解密    
    var decrypted = CryptoJS.AES.decrypt(encryptedData, key, {
      asBpytes: true,
      iv: iv,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    });
    var decryptResult = CryptoJS.enc.Utf8.stringify(decrypted).toString();
    var value = JSON.parse(decryptResult);
  } catch (err) {
    console.log(err);
  }
  return value;
}

module.exports = {
  decryptUserInfo: decryptUserInfo
}

// pages/fastlogin/fastlogin.js 
const app = getApp();
const jiemi = require('../../../utils/decryption.js');
Component({
  /**
   * 组件的初始数据
   */
  data: {
    nickname: '',
    photopath: '',
    gender: ''
  },

  /**
   * 组件的方法列表
   */
  methods: {
    // 1.关注用户是否授权,未授权页面停留,授权后获取加密信息
    onGotUserInfo(e) {
      let that = this;
      if (e.detail.errMsg == "getUserInfo:ok") {
        console.log(e, '已经授权用户信息');
        this.getLcode(e.detail.encryptedData, e.detail.iv);
      } else {
        console.log("用户未同意授权")
      }
    },
    // 2.解密unionId
    getLcode(encryptedData, iv) {
      let that = this;
      wx.login({
        success: res => {
          if (res.code) {
            ajax.dotnetRequest({
              url: '/api/system/WX?type=2&code=' + res.code,
              method: 'get',
              success: sessionRes => {
                if (sessionRes.statusCode == 200 && sessionRes.data.session_key) {
                  //  2-1:解密unionid
                  let jiemiRes = jiemi.decryptUserInfo(sessionRes.data.session_key, encryptedData, iv);
                  let unionId = jiemiRes.unionId;
                  // 2-2:获取成功,执行登录
                  if (unionId) {
                    app.goLogin(unionId)
                  }                
                }
              },
              fail: function (error) {
                console.log(error)
              }
            });
          } else {
            console.log(res, 'code获取失败');
          }
        },
        fail: res => {
          console.log(res, 'code获取失败');
        },
        complete: res => { },
      })
    },
  }
})
···



你可能感兴趣的:(微信小程序自动登录逻辑分析与实现,页面黑白名单管理(上))