整理小程序登录状态维护笔记
迫于 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 协议》,转载必须注明作者和本文链接
顶起
楼主为啥没把token放到全局的globaldata里,而只是放到缓存里呢?