0%

小程序登陆锁


超时登录我们肯定都遇到过,PC端的解决办法经常都是返回到登录页重新登录,那小程序里面呢,要怎么实现

小程序用户在使用中,我们都知道,肯定不能退出到首页让用户重新登录,不然用户体验会很差,大致分为以下几种问题

一般情况下,小程序token不会设置的那么短

  1. 调用接口后,后端返回超时登录,这时候需要重新登录获取token

  2. 获取token后,需要重新调用之前的接口,把新的token带上,重新获取数据,让用户无感登录

  3. 如果页面同时请求了多个异步接口,只能调用一次登录的接口,否则会因为重新调用接口后,每个接口附带的token都不同,导致之前的token都失效了

带着问题,我们来看看如何解决

我们先来想想解决问题的思路

  1. 判断接口是否403超时登陆
  2. 超时登录的时候记录当前的接口请求,并重新调用登录接口
  3. 登录后重新调用刚刚记录的接口请求,并替换token
  4. 如果有多个接口,判断第一个接口调用了登录,其他接口进行等待,不继续调用登录接口

这里有几个难点,如何知道登录完成了,再去调用之前的接口,如何确保其他接口可以在登录完成后才继续执行

直接看代码,注释和解释都在代码里

首先创建request.js文件和login.js文件

request.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import axios from 'axios'
import { refreshLogin} from "./login.js";

let DELAYED = 1000; // 设置队列延时
// 创建axios实例
const service = axios.create({
baseURL: process.env.BASE_API, // api 的 base_url
timeout: 60000 // 请求超时时间
})
// request拦截器
service.interceptors.request.use((config) => {

try{
config.data = JSON.parse(config.data)
}catch(e){
//TODO handle the exception
}
if(config.data.token == undefined){
config.data.token = uni.getStorageSync('LOGIN_TOKEN')
}
return config
},
(error) => {
Promise.reject(error)
}
)

// response 拦截器
service.interceptors.response.use(
(response) => {
return new Promise((resolve, reject) => {
const res = response.data
// 先判断最外层
if (res.errno !== '10000') {
// 无效TOKEN,如过期等,则执行登出操作
if(res.errno == '90005') {} else {
// 统一报错处理
uni.hideToast();
uni.showToast({
title: res.errmsg,
duration: 5000,
icon: 'none'
});
}
resolve(response.data)
} else if (res.data[0].sub_errno !== '10000') {
if(res.data[0].sub_errno == '403') {
// 这里需要重新登录
const app = getApp();
const LOGIN_SWITCH = app.globalData.LOGIN_SWITCH;// login锁,确保没有登录冲突
if(LOGIN_SWITCH){ //如果已锁说明有在重新登录,只需要等待一下 继续请求
setTimeout(() => {
response.config.data = JSON.parse(response.config.data)
response.config.data.token = uni.getStorageSync('LOGIN_TOKEN')
resolve(service(response.config))
}, DELAYED);
}else{
//重新登录并继续请求
refreshLogin().then(()=>{
response.config.data = JSON.parse(response.config.data)
response.config.data.token = uni.getStorageSync('LOGIN_TOKEN')
resolve(service(response.config))
})
}
return;
} else {
uni.hideToast();
resolve(response.data)
return;
}
} else {
// 正常数据返回
resolve(response.data)
}
})
},
(error) => {
let errorObj = {
data: [],
errmsg: '数据返回失败!',
errno: '3289'
}

if (error && error.response) {
switch (error.response.status) {
case 400: errorObj.message = '(c1400)' ; break; // 请求错误
case 401: errorObj.message = '(c1401)'; break; // 未授权
case 403: errorObj.message = '(c1403)'; break; // 拒绝访问
case 404: errorObj.message = '(c1404)'; break; // 请求出错
case 408: errorObj.message = '(c1408)'; break; // 请求超时
case 500: errorObj.message = '(c1500)'; break; // 服务器错误
case 501: errorObj.message = '(c1501)'; break; // 服务未实现
case 502: errorObj.message = '(c1502)'; break; // 网络错误
case 503: errorObj.message = '(c1503)'; break; // 服务不可用
case 504: errorObj.message = '(c1504)'; break; // 网络超时
case 505: errorObj.message = '(c1505)'; break; // HTTP版本不受支持
default: errorObj.message = `连接出错(${error.response.status})!` // 连接出错
}
}else{
errorObj.message = '(c3289)'
}
uni.hideToast();
uni.showToast({
title: '网络繁忙,请稍后再试',
duration: 2000,
icon: 'none'
});
Promise.reject(error)
}
)
//用来适配uniapp的语法
axios.defaults.adapter = function(config) {
return new Promise((resolve, reject) => {
var settle = require('axios/lib/core/settle');
var buildURL = require('axios/lib/helpers/buildURL');
uni.request({
method: config.method.toUpperCase(),
url: buildURL(config.url, config.params, config.paramsSerializer),
// url: config.baseURL + buildURL(config.url, config.params, config.paramsSerializer),
header: config.headers,
data: config.data,
dataType: config.dataType,
responseType: config.responseType,
sslVerify: config.sslVerify,
complete: function complete(response) {
response = {
data: response.data,
status: response.statusCode,
errMsg: response.errMsg,
header: response.header,
config: config
};
settle(resolve, reject, response);
}
})
})
}

export default service

login.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import axios from './request.js'
import utils from './utils.js'
import api from './api.js'
/**
* 重新登录======token不存在或过期
*/
export const refreshLogin = () => {
const app = getApp();
app.globalData.LOGIN_SWITCH = true;
uni.removeStorageSync('LOGIN_TOKEN');
return new Promise(async (resolve, reject) => {
try {
const code = await getWxCode();
let json = {
action: 'weixinLogin',
wxcode: code
}
axios.request({
url: api.url,
method: "POST",
data: json,
timeout: 10000
}).then((response)=>{
let token = response.data[0].token;
uni.setStorageSync('LOGIN_TOKEN', token);
app.globalData.LOGIN_SWITCH = false;
resolve(token);
}).catch((err)=>{
reject(err)
})
} catch (err) {
reject(err)
}
})
}

/**
* 获取 小程序的code
*/
export const getWxCode = () => {
return new Promise((resolve, reject) => {
uni.login({
success(res) {
if (res.code) {
resolve(res.code);
} else {
utils.toast('登录失败,请重新进入小程序')
}
}
})
})
}

简单来说,就是当超时的时候,只要有重新登录,就上个锁,其他的接口一直轮询等待

以上就是我对登陆锁的一些理解,如果文章由于我学识浅薄,导致您发现有严重谬误的地方,请一定在评论中指出,我会在第一时间修正我的博文,以避免误人子弟。

-------------本文结束感谢您的阅读-------------
没办法,总要恰饭的嘛~~