整理小程序登录状态维护笔记

迫于 XX 领导的 XX, 19天边学边做, 完成一个小程序的体验版(前端 + 接口 + 没有专业的产品经理,边做还得边跟 XX 领导捋逻辑)。 毕竟第一次接触小程序,坑与不坑就不说了,毕竟很多所谓的坑也只是自己没有经验。但是,其中最让我崩溃的就是维护登录状态,网上讲解这个流程的文章真心不少,小程序的登录时序图几乎没篇文章必备的,但是啥的就不说了,我就说我最后熬了大半个通宵,参考很多大神的文章,最后理出来思路吧。目前思路虽然清晰了,但是方案依然不完美,希望后面脑子能好用点,彻底想明白。。。。。。我专门把所有的代码都贴出来,保证新手直接用能跑通先,后面随着理解的深入再慢慢优化吧。


正文:

维护登录状态的的逻辑, 进入小程序后, 先用微信的 wx.checkSession() 函数判断 session 是否过期, 如果没有过期, 再检查本地是否有 token 缓存, 如果有缓存, 就取出来. 如果本地没有缓存, 就执行一整套登录逻辑, 这里登录的逻辑写在 utils/util.js 文件中.

根据小程序的生命周期, 通常情况下会先执行 app.js 文件. 我在这个文件中就完成一个目标, 完成用户登录并获取 token, 也就是说不管新老用户, 都会给予判断, 这里应该有三种判断条件:

第一种是用户第一次进入小程序, 通过微信的 wx.checkSession 自然是会验证失败, 这时就从头执行登录流程, 返回 token.
第二种是 wx.checkSession 验证通过, 但是本地 token 没有存 token, 也从头执行登录流程 ( 咳咳, 但是我目前接口返回的 token 是没有过期时间的, 汗, 因为我现在还想不好该怎么设置这个过期时间, 搞不定啊 )
第三种就是wx.checkSession 验证通过, 本地 token 也取到, 就万事大吉, 直接进入下一步. 我在首页之前, 加了一个 login 页面, 放置用户授权的 button, 也就是说, 用户登录之后, 就必须授权获取头像, 昵称等信息. 以下是 app.js 文件的代码:

//引入公共函数文件
var _js = require("./utils/util.js");

// 定义全局变量(必须在这里定义, 后面才能用)
App({
    globalData: {
        userInfo: null, // 类型是对象, 所以默认值设为 null
        token: '', // 类型是字符串, 所有默认值设为空字符串
    },

  onLaunch: function () {
    // 展示本地存储能力, 这是小程序代码自带的
    var logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)

    var that = this;

    // 查看 session_key 是否过期
    wx.checkSession({
      success() {
        // session_key 未过期,并且在本生命周期一直有效
          try {
              const value = wx.getStorageSync('token');
              if (value) {
                  console.log('第一次从本地获取 token: ');
                  console.log(value);
                  // 如果本地没有 token, 需要重新走登录流程, 目前就是获取到永不过期的 token, 并存在本地
                 if (!res.data) {
                      console.log('!!!!!');
                      _js.login()
                  } else {
                      that.globalData.token = value // 取出 token, 赋给全局变量
                      console.log('微信和第三方 session 都有, 获取到第三方 token:' + value)
                  }
              }
          } catch (e) {
              console.log('fail');
              _js.login()
          }
      },
      fail() {
        console.log('微信 session 失效')
        // session_key 已经失效,需要重新执行登录流程, 目前就是获取到永不过期的 token, 并存在本地
        _js.login()
      }
    })
  },
放置公共函数的 utils/util.js 文件
var login = function() {
  wx.login({ // 通过这个命令获取 code, 这个 code 5 分钟有效, 只能用一次
    success: res => {
      console.log(res);
      var that = this; 

      // 发送 res.code 到后台换取 openId, sessionKey, unionId
      wx.request({
        url: 'https://reading-api.oeaudio.com/api/user-info', //接口地址
        data: { code: res.code },
        header: {
          'content-type': 'application/json' //默认值
        },
        success: function (res) {
          console.log('token:' + res.data)
          // 请求成功后, 把后台返回的 token 缓存起来
          wx.setStorageSync('token', res.data)
        },
      })
    }
  })
}

// 输出这个函数
module.exports = {
  login: login,
}

进入 login 页面的生命周期.

在 app 的应用生命周期中, 无论如何我们都获取到 token 了, 也就是拿到了用户的 openid, 已实现用户登录. 下面开始进入 login 页面的声明周期. 这里唯一的目标就是引导用户完成授权, 获取到 userInfo 并缓存到全局.

login 页面的 js 文件
const app = getApp();

Page({
  data: {
    //判断小程序的API,回调,参数,组件等是否在当前版本可用。
    canIUse: wx.canIUse('button.open-type.getUserInfo'),
  },
  onLoad: function () {
    var that = this;

    // 查询一下用户是否授权了 "scope.userInfo" 这个 scope, 如果已授权, 就直接拿到用户信息, 并存储在本地缓存, 然后跳转到 index 页面
wx.getSetting({
      success(res) {
        console.log('get setting:')
        console.log(res)
        if (res.authSetting['scope.userInfo']) {
          // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
          wx.getUserInfo({
              lang:'zh_CN', // 设置获取用户信息的语言
            success(res) {
              wx.setStorageSync('userInfo', res.userInfo)
              app.globalData.userInfo = res.userInfo;

              //用户已经授权过,直接跳转到首页
              wx.redirectTo({
                url: "../index/index"
              })

              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
              // 所以此处加入 callback 以防止这种情况
              if (that.userInfoReadyCallback) {
                that.userInfoReadyCallback(res)
              }
            }
          })
        }
      },
    })

  // 如果页面加载时, 用户没有授权过, 程序就会走到这里, 显示让用户授权的 button
bindGetUserInfo: function (e) {
    //用户按了允许授权按钮, 就拿到用户的信息了
    var that = this; 
    if (e.detail.userInfo) {
      try {
        const value = wx.getStorageSync('token');
        if (value) {
          that.setData({
            'token': value
          });
        }
      } catch (e) {
        // Do something when catch error
      }

      //插入登录的用户的相关信息到数据库
      wx.request({
        url: 'https://reading-api.oeaudio.com/api/user-store', //接口地址
        data: {
          nickname: e.detail.userInfo.nickName,
          avatar: e.detail.userInfo.avatarUrl,
          token: that.data.token,
        },
        header: {
          'content-type': 'application/json' //默认值
        },
        method: 'POST',
        success: function (res) {
          console.log('保存用户信息' + res.data.msg)
          wx.setStorageSync('userInfo', e.detail.userInfo)

          // 同时把用户信息给全局变量, 方便后面用
          app.globalData.userInfo = e.detail.userInfo
        }
      })

      //授权成功,保存数据之后, 就会跳转进入小程序首页 index 页面, 此时, 缓存中已经有了用户的微信相关的信息
      wx.redirectTo({
        url: "../index/index"
      })
    } 
  },  
})
login 页面的 wxml 代码
<view wx:if="{{canIUse}}">
    <view class='header'>
        <image src='../resources/company-avatar.png'></image>
    </view>

    <view class='content'>
        <view>申请获取以下权限</view>
        <text>获得你的公开信息(昵称,头像等)</text>
    </view>

    <button class='bottom' type='primary' open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="bindGetUserInfo">
        授权登录
    </button>
</view>

<view wx:else>请升级微信版本</view>
login 页面的 wxss 代码
.header {
    margin: 90rpx 0 90rpx 50rpx;
    border-bottom: 1px solid #ccc;
    text-align: center;
    width: 650rpx;
    height: 300rpx;
    line-height: 450rpx;
}

.header image {
    width: 200rpx;
    height: 200rpx;
}

.content {
    margin-left: 50rpx;
    margin-bottom: 90rpx;
}

.content text {
    display: block;
    color: #9d9d9d;
    margin-top: 40rpx;
}

.bottom {
    border-radius: 80rpx;
    margin: 70rpx 50rpx;
    font-size: 35rpx;
}

经过前面的铺垫, 我们的本地缓存和全局变量中都已经有了 token 和 userInfo 信息, 后面的页面就可以随用随取了.

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

楼主为啥没把token放到全局的globaldata里,而只是放到缓存里呢?

4年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!