一、封装axios请求
import libAxios from 'axios'
// axios网络请求封装请求开始
const axios = libAxios.create({
baseURL: process.env.API_ROOT,
timeout: 15000,
withCredentials: false,
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
'Access-Control-Allow-Methods': 'OPTIONS, GET, POST'
}
})
export default axios其中,baseURL的地址在文件quasar.conf.js里配置
build配置里
env: ctx.dev
? {
// so on dev we'll have
API_ROOT: JSON.stringify('/v1')
}
: {
// and on build (production):
API_ROOT: JSON.stringify('http://***.***.**.**:8088/v1')
}设置代理devServer
devServer: {
https: false,
port: 8080,
open: true, // opens browser window automatically
proxy: {
'/v1': {
target: 'http://***.***.***.**:8088',
changeOrigin: true
}
}
},以上就是axios的封装
使用封装后的axios
配置api,新建目录,新建api文件
//我封装的位置,引进来
import axios from 'src/lib/axios'
export const login = ({ mobile, password}) => {
const data = {
mobile,
password,
}
return axios({
url: '/login',
method: 'post',
data
})
}使用
<script>
import { login } from 'src/api/common'
export default {
name: 'Login',
data () {
return {
form: {
mobile: '',
password: ''
}
}
},
methods:{
onSubmit () {
login(this.form).then(data => {
console.log(data)
})
}
}
}
</script>
二、拦截器+token刷新
我这里设置拦截器的目的是实现保持登录功能,我的token一小时过期,所以需要在快过期的时候通过refreshToken(一般十几天过期)重新获取token,当refreshToken过期时提醒用户重新登录或者直接跳到登录页
1.先看一下我的两个接口(登录接口、token刷新接口)所返回的数据,只看一下模拟的接口,后台的具体实现暂且不表
登录接口login——返回access_token作为令牌,refresh_token作为刷新access_token的凭据

token刷新接口refresh

2.登录成功后对返回的数据进行处理
这里我先拿到token,然后存进vuex里,登录成功后跳转首页
这个目录不仅存了token,还存了用户的登录状态等信息
![]()
onSubmit () {
login(this.form).then(data => {
const { access_token: accessToken, refresh_token: refreshToken } = data
setAuth({
accessToken,
refreshToken
})
this.$router.push({
path: this.$route.query.redirect || '/'
})
})
}这里介绍怎么设置vuex,我的vuex目录如下,关于token的存储在user目录里

actions.js

先不管getters.js
index.js

mutations.js

state.js

在modules目录下的index.js

在store下的index.js,为了防止刷新后数据消失,我使用了插件vuex-persistedstate,
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
import createPersistedState from 'vuex-persistedstate'
import SecureLS from 'secure-ls'
const ls = new SecureLS({
encodingType: 'aes',
isCompression: false
})
Vue.use(Vuex)
const store = new Vuex.Store({
modules,
// enable strict mode (adds overhead!)
// for dev mode only
strict: process.env.DEV,
plugins: [
createPersistedState({
storage: {
getItem: (key) => ls.get(key),
setItem: (key, value) => ls.set(key, value),
removeItem: (key) => ls.remove(key)
},
paths: [
'user',
'obs.showTransList',
'obs.token'
]
})
]
})
export default store
然后在src/lib/utils文件里配置用户状态的存储,这个文件就引入登录页
import * as jwtDecode from 'jwt-decode'//jwt
import store from 'src/store'//vuex
//先放在这,对于状态存储暂时没有用,由于后面token过期的判断
export const decodeJwt = (token, header = false) => {
return jwtDecode(token, { header })
}
export const setAuth = ({ accessToken, refreshToken }) => {
const decodedToken = jwtDecode(accessToken)
console.log('登录状态', decodedToken)
const user = {
id: decodedToken.aud,
exp: decodedToken.exp,
iat: decodedToken.iat,
iss: decodedToken.iss,
jti: decodedToken.jti,
nickName: decodedToken.nick_name
}
store.dispatch('user/setAuth', {
accessToken,
refreshToken,
user
})
}
以上就是对login接口返回的token数据进行存储的全部过程
3.设置拦截器拦截请求和响应,这里的拦截器主要为token刷新准备,在设置拦截器之前选配置好token刷新api、获取token、token过期判断等,现在一步步来
(1)刷新token的api,我放在配置login api的文件里
// 刷新token
export const refreshToken = (refreshToken) => {
const data = {
refresh_token: refreshToken
}
return axios({
url: '/refresh',
method: 'post',
data
})
}(2)关于token过期、获取token、清除token等的配置放在文件src/lib/utils里
- 读取token
//读取放在vuex里的内容
export const getToken = () => {
return store.state.user.token || {}
}- 删除token
export const removeAuth = () => {
store.dispatch('user/removeAuth')
}- 判断accessToken是否(即将)过期,这里就用到之前暂放的decodeJwt函数
export const isTokenExpired = () => {
const { accessToken } = getToken()
if (!accessToken) {
return false
}
const decodedToken = decodeJwt(accessToken)
// 小于0已过期||小于10分钟即将过期
return parseInt(decodedToken.exp) - (Date.now() / 1000) < 10 * 60
}
- 判断refreshToken是否过期
export const isRefreshTokenExpired = () => {
const { refreshToken } = getToken()
if (!refreshToken) {
return true
}
const expiredTime = parseInt(refreshToken.slice(refreshToken.lastIndexOf('_') + 1))
const nowTime = Date.now() / 1000
// 检验本地时间差
return expiredTime < nowTime
}(3)在axios.js文件里引入配置内容
须在axios.js引入这些文件,其中simpleNotify是警告提示的作用,暂时不表

引入
![]()
(4)拦截器的前一步准备

(5)拦截器配置——请求拦截

// 拦截请求
axios.interceptors.request.use(
config => {
// 全局loading
LoadingBar.increment(0.5)
LoadingBar.start()
//获取token
const { accessToken, refreshToken } = getToken()
//accexxtoken是否存在
if (accessToken) {
//给请求添加token令牌
config.headers.Authorization = `Bearer ${accessToken}`
// 判断refershToken是否过期,过期就给出重新登录提示
if (isRefreshTokenExpired()) {
LoadingBar.stop()
manualLogin()
return Promise.reject('refreshToken expired')
}
// 主动刷新token——在accesstoken临近过期的时候
if (isTokenExpired() && !config.url.includes('/refresh') && !config.url.includes('/login')) {
// 判断是否正在刷新token
if (!window.isRefreshing) {
console.log('主动刷新token')
window.isRefreshing = true
//请求token刷新接口,获取新的accesstoken
getRefreshToken(refreshToken)
.then(data => {
const { access_token: accessToken, refresh_token: refreshToken } = data
if (!accessToken) {
throw new Error('刷新失败')
}
console.log('token主动刷新成功: ', data)
window.isRefreshing = false
//将获取的新accesstoken加在请求上
config.headers.Authorization = `Bearer ${accessToken}`
//并保存在vuex里
setAuth({
accessToken,
refreshToken
})
// 执行刷新token期间挂起的api请求
onRefreshed(accessToken)
// 执行onRefreshed函数后清空挂起的请求
refreshSubscribers = []
})
.catch(err => {
console.log('token主动刷新失败', err)
manualLogin()
})
}
// 把token刷新期间的每一个请求都放在数组中
return new Promise((resolve) => {
subscribeTokenRefresh((token) => {
config.headers.Authorization = `Bearer ${token}`
// 将请求挂起
resolve(config)
})
})
}
}
return config
},
error => {
console.log('request error: ', error)
simpleNotify()
return Promise.reject(error)
}
)
(6)拦截器配置——响应拦截

// 拦截响应
axios.interceptors.response.use(
response => {
LoadingBar.stop()
// 处理响应
const { config, data, data: { code, message } } = response
//200 成功
if (code === 200) {
return Promise.resolve(data.data)
} else if (code === 401) {
// token认证失败,被动刷新token
//获取refreshToken —— 用作获取新accesstoken的凭据
const { refreshToken } = getToken()
if (refreshToken) {
if (!config.url.includes('/refresh')) {
if (!window.isRefreshing) {
console.log('被动刷新token')
window.isRefreshing = true
getRefreshToken(refreshToken)
.then((data) => {
const { access_token: accessToken, refresh_token: refreshToken } = data
if (!accessToken) {
throw new Error('刷新失败')
}
console.log('token被动刷新成功: ', data)
window.isRefreshing = false
config.headers.Authorization = `Bearer ${accessToken}`
setAuth({
accessToken,
refreshToken
})
// 执行刷新token期间挂起的api请求
onRefreshed(accessToken)
// 执行onRefreshed函数后清空挂起的请求
refreshSubscribers = []
})
.catch((err) => {
console.log('token被动刷新失败', err)
// token被动刷新失败
manualLogin()
})
}
// 把token刷新期间的每一个请求都挂起
return new Promise((resolve) => {
subscribeTokenRefresh((token) => {
config.url = config.url.replace(config.baseURL, '')
config.headers.Authorization = `Bearer ${token}`
// 重新请求
axios(config)
})
})
}
} else {
manualLogin()
}
} else {
simpleNotify({ message })
return Promise.reject(data)
}
},
error => {
LoadingBar.stop()
console.log(error)
return Promise.reject(error)
}
)