Vue axios 刷新 Jwt 最近做项目,一个需求就是Jwt token的失效的时候,如果用户还在操作,那么希望无感刷新token,让用户的操作不受影响。
两种核心思路:
前端发起ajax请求 => 后端发现jwt已经过期,返回50014状态码 => 前端拦截响应数据,并发起刷新token的请求 => 拿到最新的jwt和refresh,保存到本地 => 拿到最新的jwt去进行刚刚未请求成功的接口 => 获取到刚刚请求的结果,覆盖第一次请求失败(状态码为50014)的响应数据 => 返回第二次请求的结果.
前端发起ajax请求 => 后端发现jwt已经过期,返回50014状态码 => 前端拦截响应数据,并发起刷新token的请求 => 后端返回了10007的状态码(这个时候代表refreshJwt也过期了,需要进行重新登录了) => 真正的过期了,需要跳转到登录界面.
新建一个refreshJwt.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 import { getRefreshToken, setRefreshToken, setToken } from './auth' import axios from 'axios' import store from '../store' import { MessageBox } from 'element-ui' import i18n from '@/lang' /** * 刷新JWT设计思路: * * 第一种情况: 前端发起ajax请求 => 后端发现jwt已经过期,返回50014状态码 => 前端拦截响应数据,并发起刷新token的请求 => 拿到最新的jwt和refresh,保存到本地 => 拿到最新的jwt去进行刚刚未请求成功的接口 => 获取到刚刚请求的结果,覆盖第一次请求失败(状态码为50014)的响应数据 => 返回第二次请求的结果. * 第一种情况: 前端发起ajax请求 => 后端发现jwt已经过期,返回50014状态码 => 前端拦截响应数据,并发起刷新token的请求 => 后端返回了10007的状态码(这个时候代表refreshJwt也过期了,需要进行重新登录了) => 真正的过期了,需要跳转到登录界面. * @returns {Promise<void>} */ export default async() => { // process.env.BASE_API是项目环境API const url = process.env.BASE_API + '/refreshJwt/' + getRefreshToken() await axios.post(url).then (res => { if (res.data.code === 10007) { // 身份过期,请重新登录 MessageBox.confirm( 'Token失效,你已被登出,可以取消继续留在该页面,或者重新登录' , '确定登出' , { confirmButtonText: '重新登陆' , cancelButtonText: '取消' , type : 'warning' } ).then (() => { store.dispatch('FedLogOut' ).then (() => { location.reload() // 为了重新实例化vue-router对象 避免bug }) }) } else { // 设置token setToken(res.data.data.token) store.commit('SET_TOKEN' , res.data.data.token) // 设置refreshToken setRefreshToken(res.data.data.refreshToken) store.commit('SET_REFRESH_TOKEN' , res.data.data.refreshToken) } }).catch(error => { console.log(error) }) }
配置拦截器 代码如下:
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 import axios from 'axios' import i18n from '@/lang' import { Message, MessageBox } from 'element-ui' import store from '../store' import { getToken, getRefreshToken } from './auth' import refreshJwt from './refreshJwt' let isLock = true // 创建axios实例 const service = axios.create({ baseURL: process.env.BASE_API, // api 的 base_url timeout: 50000, // 请求超时时间, headers: { 'Content-Type' : 'application/json;charset=utf-8' } // withCredentials : true }) /** * 通用请求拦截配置 * @param {*} config */ const axiosConf = (config) => { if (store.getters.token) { config.headers['X-Token' ] = getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 } return config } // request 拦截器 service.interceptors.request.use(axiosConf, error => { console.log(error) return Promise.reject(error) }) // response 拦截器 service.interceptors.response.use( async response => { /** * code为非0是抛错 可结合自己业务进行修改 */ let data = {} const res = response.data const code = Number(res.code) if (code !== 0) { // 50014:Token 过期了; if (code === 50014) { if ((getRefreshToken() !== 'undefined' && getRefreshToken()) && isLock) { // 异步刷新JWT await refreshJwt() // 这里防止并发的时候造成死循环,所以要加锁 isLock = false // 刷新完成,继续之前的请求 response.config.headers['X-Token' ] = getToken() const result = await axios.request(axiosConf(response.config)) if (result) { data = result.data isLock = true } } else { // 身份过期,请重新登录 MessageBox.confirm( 'Token失效,你已被登出,可以取消继续留在该页面,或者重新登录' , '确定登出' , { confirmButtonText: '重新登陆' , cancelButtonText: '取消' , type : 'warning' } ).then (() => { store.dispatch('FedLogOut' ).then (() => { location.reload() // 为了重新实例化vue-router对象 避免bug }) }) } } else { // 消息提示 message(res.message) } } else { data = response.data } return data }, error => { console.log('错误信息:' + error) message('哎呀~ (ಥ﹏ಥ)网络又开小差了,请稍后刷新重试!' ) return Promise.reject(error) } ) /** * 消息提醒 * @param msg */ export function message(msg) { Message({ message: msg, type : 'error' , showClose: true , duration: 5 * 1000 }) } export default service
小生的刷新token思路如此,如阁下有更好的思路,方便分享,请留言哦。