小程序一般是在app onLaunch程序初始化时,检测用户是否登录过,(查看localStorage中是否有登录标志),然后wx.checkSession() 接口来判断session_key是否过期作为用户登录状态是否过期,没有过期就直接进行下一步,过期了先去重新登录再进行下一步操作。
登录系统设计方面,无论是小程序还是app分为两种:
一种是首页中是否有和用户相关的信息获取展示模块(比如获取用户的报名课程,任务,扇贝阅读app,不背单词app),这种需要在程序启动时检测session是否过期,如果过期了,这时应该在进入首页前,就截断用户操作(不然,用户获取不到属于他的正确信息),界面给出一个提示进行重新登陆或者路由跳转到重新登陆界面,登录完成之后再返回到首页。
另一种是首页中没有和用户相关的信息(比如获取用户的报名课程,大多都是系统的推荐相关,分类展示信息,比如美团app),用户的信息一般放在 其他的tab(我的页面)中, 这种就不需要做一个明显的session_key过期的重新登陆模式,因为之所以要重新登录主要是和用户的信息有关系,首页不需要获取用户信息,就没必要做出一个提示要用户重新登录,一般是在用户无感知的情况下,系统自动进行了重新登录,本地存储最新的登录标志。当用户手动切换到到其他tab的时候,app本地已经进行过重新登陆了,用户可以不受干扰的继续使用app。
1. 思路
1)用户登录态过期时间如何设置?
这里我们在前端进行控制,即利用wx.checkSession()
接口来判断session_key
是否过期来作为用户登录态是否过期的标志。如果过期了,则跳转到统一的登录页面引导用户点击按钮重新授权登录,重新登录之后session_key
会刷新,相当于在获取用户最新信息的同时重新设定了过期时间。
(2)onShow() 与onLoad()
小程序js 中有onShow
与onLoad
两种事件。两种事件的区别就在于onLoad 每次打开小程序只加载一次,跳转到其他页面再回来的时候这个事件就不会再触发。而onShow 则每次进入页面都会触发,所以我们在进入每个页面检查用户登录态是否过期的代码需要放在onShow
中。
(3)重新登录过程分析
如果用户登录态过期,则需要进行重新登录。前端引导用户点击按钮触发getUserInfo
获取最新用户信息 -> 前端调用wx.login()
获取code
-> 前端将code
发送给后端获取openid
和seesion_key
-> 后端写session
并返回对应session
的唯一标志 -> 前端存储这个唯一标志。
2. 代码实例
在每个页面的onShow 事件中添加以下代码来检查当前用户登录态是否过期:
`wx.checkSession({`` ``success: ``function` `() {`` ``//session_key 未过期,并且在本生命周期一直有效`` ``return` `;`` ``},`` ``fail: ``function` `() {`` ``// session_key 已经失效,需要重新执行登录流程`` ``wx.navigateTo({`` ``url: ``"/pages/authorize/index"`` ``})`` ``}``})`
因为进入每个页面中都需要进行用户登录态是否过期的检查,所以需要有一个公共的授权页面,当检查不同过的时候,就跳转到这个授权页面引导用户重新进行授权,授权页面authorize 代码如下:
wxml
<view class="container">
<view style='width:100%;padding-left:30rpx;font-size: 28rpx;margin-top:30rpx;'>1、同意当前小程序获取我的微信头像;</view>
<view style='width:100%;padding-left:30rpx;font-size: 28rpx;margin-top:30rpx;'>2、同意当前小程序获取我的微信昵称等其他信息;</view>
<button open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo" class="save-btn">授权登录</button>
</view>
wxss
page{
height: 100%;
}
.container{
background-color: #f5f5f9;
justify-content: initial;
}
.save-btn{
width: 690rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
margin-top:30rpx;
border-radius: 6rpx;
box-sizing: border-box;
background-color: #e64340;
color:#fff;
}
// pages/authorize/index.js
var app = getApp();
let Domain = app.globalData.domain;
Page({
/**
* 页面的初始数据
*/
data: { },
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) { },
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () { },
/**
* 生命周期函数--监听页面显示
*/
onShow: function () { },
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () { },
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () { },
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () { },
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () { },
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () { },
bindGetUserInfo: function (e) {
// 获得最新的用户信息
if (!e.detail.userInfo){
return;
}
wx.setStorageSync('userInfo', e.detail.userInfo)
this.checkSessionAndLogin();
},
/*
这里使用openid 作为与后端session 连接的标志
检查是否存在openid,即之前是否登录过
如果登录过,检查session_key 是否过期
如果过期了,remove openid 重新执行login 并将用户信息发送到服务器端更新
如果没过期则返回
如果没登录过则执行login 并将用户信息发送到服务器更新
*/
checkSessionAndLogin: function () {
let that = this;
let thisOpenId = wx.getStorageSync('openid');
// 已经进行了登录,检查登录是否过期
if (thisOpenId) {
console.log('have openid')
wx.checkSession({
success: function () {
//session_key 未过期,并且在本生命周期一直有效
wx.navigateBack({});
},
fail: function () {
console.log('but session_key expired');
// session_key 已经失效,需要重新执行登录流程
wx.removeStorageSync('openid'); //这里会让openid在本地不存在,通不过if判断,从而走else逻辑,进行登录
that.checkSessionAndLogin();
}
})
} else {
// 没有进行登录则先进行登录操作
console.log('do not have openid');
that.loginAndGetOpenid();
}
},
// 执行登录操作并获取用户openId
loginAndGetOpenid: function () {
console.log('do login and get openid');
let that = this;
wx.login({
success: function (res) {
if (res.code) {
wx.request({
url: Domain + '/user/wx_login',
data: {
code: res.code
},
success: function (res) {
res = res.data;
console.log(res)
// 保存openId,并将用户信息发送给后端
if (res.code === 0) {
wx.showModal({
title: 'set openid',
content: res.data,
})
wx.setStorageSync('openid', res.data);
that.sendUserInfoToServer();
} else {
wx.showModal({
title: 'Sorry',
content: '用户登录失败~',
})
}
}
})
}
}
})
},
sendUserInfoToServer: function () {
console.log('now send user info to server');
let userInfo = wx.getStorageSync('userInfo');
let thisOpenId = wx.getStorageSync('openid');
userInfo.openid =thisOpenId;
wx.request({
url: Domain + '/user/updateUser',
method: 'POST',
dataType: 'json',
data: userInfo,
success: function (res) {
res = res.data;
if (res.code === 0) {
wx.navigateBack({});
} else {
wx.showModal({
title: 'Sorry',
content: '同步信息出错~',
})
}
}
})
}
})
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid是相同的。
绑定了开发者帐号的小程序,可以通过以下途径获取 UnionID。
- 调用接口 wx.getUserInfo,从解密数据中获取 UnionID。注意本接口需要用户授权,请开发者妥善处理用户拒绝授权后的情况。
- 如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号。开发者可以直接通过 wx.login +
code2Session
获取到该用户 UnionID,无须用户再次授权。 - 如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用。开发者也可以直接通过 wx.login +
code2Session
获取到该用户 UnionID ,无须用户再次授权。 - 用户在小程序(暂不支持小游戏)中支付完成后,开发者可以直接通过
getPaidUnionId
接口获取该用户的 UnionID,无需用户授权。注意:本接口仅在用户支付完成后的5分钟内有效,请开发者妥善处理。 - 小程序端调用云函数时,如果开发者帐号下存在同主体的公众号,并且该用户已经关注了该公众号,可在云函数中通过 cloud.getWXContext 获取 UnionID。
- 小程序端调用云函数时,如果开发者帐号下存在同主体的公众号或移动应用,并且该用户已经授权登录过该公众号或移动应用,也可在云函数中通过 cloud.getWXContext 获取 UnionID。