步骤如下:
1-token过期根据refresh_token获取新的token 重新获取数据
2-创建一个新的axios实例 【使用request防止再次进入请求拦截和请求响应而进入死循环】
3-根据请求相应的响应值 是不是401 是:说明token过期
然后进行判断store中的 user :{token:’*****’,refresh_token:’******’}中的 refresh_token和user对象是否存在 ,如果不存在说明之前没有登录过,直接去登录
4-使用新创建的axios 实例对象 requestFreshToken 发送新的请求 headers中的口令携带的是 refresh_token
5-获取token之后 将值重新赋值给user中的token
6-将user重新存入store中
7-重新获取刚才因为token失效而没有获取的数据 直接使用request 参数 来自error对象中【这里保存了之前token失效的请求数据】
具体实现 代码如下:
import Vue from 'vue'
import axios from 'axios'
import router from '@/router'
import {
ACCESS_TOKEN,
FRESH_TOKEN
} from '@/store/mutation-types'
import {
Message
} from 'element-ui';
// somefile.js
import ls from '@/utils/localStore.js';
const defaultSettings = require("@/settings.js");
let apiBaseUrl = process.env.VUE_APP_BASE_API
let isRefreshing = false //是否正在刷新token
let callbacks = [] //失效后同时发送请求的容器 --- 缓存接口
// 刷新 token 后, 将缓存的接口重新请求一次
function onAccessTokenFetched() {
callbacks.forEach(callback => {
callback()
})
callbacks = [] // 清空缓存接口
}
// 添加缓存接口
function addCallbacks(callback) {
callbacks.push(callback)
}
// 请求函数
function request(url, options) {
const token = ls.get(ACCESS_TOKEN)
const defaultOptions = {
withCredentials: true,
url: url,
baseURL: apiBaseUrl,
};
const newOptions = {
...options,
...defaultOptions
};
newOptions.headers.token = token;
return axios.request(newOptions)
.then(checkStatus)
.catch(error => console.log(error));
}
function checkStatus(error) {
if (error.response) {
const response = error.response
const refreshToken = ls.get(FRESH_TOKEN);
if (response && response.status == 401) {
// 刷新token的函数,这需要添加一个开关,防止重复请求
if (!isRefreshing) {
isRefreshing = true;
axios({
baseURL: apiBaseUrl,
url: `/reLogin/${refreshToken}`,
method: 'post'
}).then(response => {
const res = response.data;
isRefreshing = false;
if (res.code == '200') {
ls.set(ACCESS_TOKEN, res.data.access_token)
onAccessTokenFetched()
} else {
// 登出
ls.clear()
router.replace({
path: '/login'
})
}
}).catch(error => {
console.log("刷新token异常", error)
// 跳转到首页
ls.clear()
console.log('request router', router)
router.replace({
path: '/login'
})
});
}
isRefreshing = true;
const options = response.config;
const url = options.url.replace(process.env.VUE_APP_BASE_API, '')
// 这个Promise函数很关键
const retryOriginalRequest = new Promise((resolve) => {
console.log('resolve', resolve)
addCallbacks(() => {
resolve(request(url, options))
})
});
return retryOriginalRequest;
} else {
// message 诺到里面,401不报错
Message.closeAll()
Message({
message: response.data.msg,
type: 'error'
}, true)
return Promise.reject(error)
}
}
return error.data;
// return error;
}
// 创建一个axios实例
const service = axios.create({
baseURL: apiBaseUrl,
withCredentials: true, // 跨域请求时发送Cookie
timeout: 50000 // request timeout
})
//请求拦截器
service.interceptors.request.use(
config => {
// 在发送请求之前做一些事情
if (ls.get(ACCESS_TOKEN)) {
// 让每个请求都带有token
config.headers['token'] = ls.get(ACCESS_TOKEN)
} else if (process.env.NODE_ENV === 'development' && defaultSettings.noToken) {
// console.log('没有token--------', process.env.NODE_ENV)
config.headers['swagger_user_name'] = defaultSettings.swagger_user_name
config.headers['swagger_user_pwd'] = 1
}
config.autoCatchErr = true;
return config
},
error => {
console.log(error) // for debug
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data;
// 增加res.response,zq-el-ui中的下载请求会用到
res.response = response;
if (response.status == 200 && response.config.params && response.config.params.no_interception_response) {
return response.data
}
// 文件下载
if (response.status == 200 && response.request.responseType == "blob" || response.request.responseType == "arraybuffer") {
return response.data
}
// 正常返回
if (response.data.body && response.data.body.rtnCode === '0000') {
return response.data.body.dataSet
}
if (res.code == 200) {
if (res.msg) {
Message({
message: res.msg,
type: 'success'
}, true)
}
return res
} else {
if (response.config.autoCatchErr) {
Message.closeAll()
Message({
message: res.msg,
type: 'error'
}, true)
console.log('请求失败', res)
//
// throw new Error('接口报错:::', res);
return res
} else {
Message({
message: res.msg,
type: 'error'
}, true)
return res
}
}
},
error => {
return checkStatus(error)
}
)
export default service