vue前端刷新token实现(亲测有效)

实现思路

这里用的请求框架是axios,所以我在这里主要是在请求拦截器里实现token刷新逻辑处理的。先大概说一下整个思路:

我们这里是在token即将过期的时候进行token刷新,而不是已经过期了才去刷新,这里即将过期的时间设置的是10分钟(这里有一个特别的点,就是我这里和后台协商,如果过期了20分钟内也让刷新token,超过过期20分钟,则判断为已过期,退出登录。所以这里请根据自身需求进行修改即可),这个时间在下面代码判断中可以根据自己项目情况自行调整。

一般一个页面同时会有很多个请求,所以我们需要建一个数组先缓存起来这些请求;除此之外,还要建一个全局标志,绑在window上,用来判断当前是否正在刷新token;待token刷新完成后,再去执行数组中缓存的所有请求,此时,数组中的请求都是带着刷新后的最新的token值作为headers去请求的。

下面直接上相关代码:

import axios from 'axios'
import { Message } from 'element-ui'
import store from '@/store'
import { getToken, setToken, getExpires, setExpires, getUserInfo, setUserInfo } from '@/utils/auth'
import { refreshToken } from "@/api/refreshToken"


// 是否正在刷新的标志
window.isRefreshing = false;
// 存储请求的数组
let cacheRequestArr = [];

// 将所有的请求都push到数组中,其实数组是[function(token){}, function(token){},...]
function cacheRequestArrHandle(cb) {
    cacheRequestArr.push(cb);
}
// 数组中的请求得到新的token之后自执行,用新的token去重新发起请求
function afreshRequest(token) {
    cacheRequestArr.map(cb => cb(token));
    cacheRequestArr = [];
}
// 判断token是否即将过期
function isTokenExpired() {
    let curTime = new Date().getTime();
    let expiresTime = Number(getExpires()) - curTime;
    // 还差10分钟即将过期或者已经过期了,但过期时间在20分钟内
	if ((expiresTime >= 0 && expiresTime < 600000) || (expiresTime < 0 && Math.abs(expiresTime) <= 1200000)) {
		return true
	}
    return false;
}

// 创建一个 axios 接口
const service = axios.create({
    baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
    timeout: 5000 // 请求超时时间设置
})

// 请求拦截器
service.interceptors.request.use(
    config => {
        // console.log(config);
        // 在请求发送之前做一些处理
        if (store.getters.token) {
            // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改
            config.headers['Authorization'] = getToken();

            // 判断token是否即将过期,且不是请求刷新token的接口
            if (isTokenExpired() && config.url !== '/admin/base/refresh') {
                // 所有的请求来了,先判断是否正在刷新token,
                // 如果不是,将刷新token标志置为true并请求刷新token.
                // 如果是,则先将请求缓存到数组中
                // 等到刷新完token后再次重新请求之前缓存的请求接口即可
                if (!window.isRefreshing) {
                    // 标志改为true,表示正在刷新
                    window.isRefreshing = true;
                    let userInfo = getUserInfo() ? JSON.parse(getUserInfo()) : '';
                    const data = {
                        uuid: userInfo.uuid,
                        expiresAt: Number(getExpires())
                    }
                    refreshToken(data).then(res => {
                        if (res.data.code == 200) {
                            // 更新cookie里的值
                            setToken(res.data.data.token, new Date(res.data.data.expiresAt));
                            setExpires(res.data.data.expiresAt, new Date(res.data.data.expiresAt));
                            setUserInfo(JSON.parse(getUserInfo()), new Date(res.data.data.expiresAt))
                            // 更新 store里的值
                            store.commit('SET_TOKEN', res.data.data.token);
                            store.commit('SET_EXPIRESAT', res.data.data.expiresAt);
                            // 将刷新的token替代老的token
                            config.headers['Authorization'] = getToken();
                            // 刷新token完成后重新请求之前的请求
                            afreshRequest(getToken())
                        } else {
                            Message({
                                message: res.data.msg,
                                type: 'error',
                            })
                            store.dispatch('resetUserCookies').then(() => {
                                location.reload() // 为了重新实例化vue-router对象 避免bug
                            })
                        }
                    }).catch(err => {
                        console.log('refreshToken err =>' + err);
                        store.dispatch('resetUserCookies').then(() => {
                            location.reload() // 为了重新实例化vue-router对象 避免bug
                        })
                    }).finally(() => {
                        window.isRefreshing = false;
                    })
                    // 下面这段代码一定要写,不然第一个请求的接口带过去的token还是原来的,要将第一个请求也缓存起来
                    let retry = new Promise((resolve) => {
                        cacheRequestArrHandle((token) => {
                            config.headers['Authorization'] = token; // token为刷新完成后传入的token
                            // 将请求挂起
                            resolve(config)
                        })
                    })
                    return retry;
                } else {
                    let retry = new Promise((resolve) => {
                        cacheRequestArrHandle((token) => {
                            config.headers['Authorization'] = token; // token为刷新完成后传入的token
                            // 将请求挂起
                            resolve(config)
                        })
                    })
                    return retry;
                }
            } else {
                return config
            }
        }
        return config
    },
    error => {
        // 请求错误处理
        Promise.reject(error)
    }
)

// 响应拦截器
service.interceptors.response.use(
    response => {
        const res = response.data;
        if (res.code == 401 || res.code == 402) {
            Message({
                message: res.msg,
                type: 'error',
                duration: 5 * 1000
            })
            store.dispatch('resetUserCookies').then(() => {
                location.reload() // 为了重新实例化vue-router对象 避免bug
            })
            return Promise.reject('error')  
        }
        return response
    },
    error => {
        Message({
                message: error.message,
                type: 'error',
                duration: 5 * 1000
            })
            // return Promise.reject(error)
    }
)

export default service

上面代码主要是请求拦截器和响应拦截器核心代码,里面添加了刷新token的逻辑代码,上面的有些代码可根据自己情况进行自定义修改,主要核心逻辑就如上面所示。

下面是上面代码里面用到的一些相关导入代码:

下面这部分代码是操作cookie相关代码

import Cookies from 'js-cookie'

// 将token写入cookie
const TokenKey = 'Token'

export function getToken() {
    return Cookies.get(TokenKey)
}

export function setToken(token, expires) {
    return Cookies.set(TokenKey, token, { expires: expires })
}

export function removeToken() {
    return Cookies.remove(TokenKey)
}


// 将expiresAt过期时间写入cookie
const expiresAtKey = 'ExpiresAt'

export function getExpires() {
    return Cookies.get(expiresAtKey)
}

export function setExpires(expiresAt, expires) {
    return Cookies.set(expiresAtKey, expiresAt, { expires: expires })
}

export function removeExpires() {
    return Cookies.remove(expiresAtKey)
}

// 将userInfo用户信息写入cookie
const userInfoKey = 'UserInfo'

export function getUserInfo() {
    return Cookies.get(userInfoKey)
}

export function setUserInfo(userInfo, expires) {
    // cookie存储对象时,必须转化为JSON字符串,否则读取不到对象里的属性值
    return Cookies.set(userInfoKey, JSON.stringify(userInfo), { expires: expires })
}

export function removeUserInfo() {
    return Cookies.remove(userInfoKey)
}

下面这部分代码是刷新token时请求后台用到接口(这个可根据自己项目情况写)

import request from '@/utils/request'

export function refreshToken(data) {
    return request({
        url: '/admin/base/refresh',
        method: 'post',
        data
    })
}

上面的所有代码我都是根据自己情况进行了一些封装处理后使用的,大家可以根据自己手上的具体项目情况来做适合自己的封装,不一定要和上面一模一样,主要的相关代码就上面这些了,具体还有疑问的地方可留言询问,我看到会回复的。

Vue实现无感刷新token的方法可以通过拦截器来实现。首先,你可以在axios的响应拦截器中判断返回的状态码是否为401,如果是的话,说明token已过期。然后,你可以设置一个变量isRefreshing来控制是否正在刷新token的状态。当第一个请求触发token过期时,isRefreshing会被设置为true,然后发送刷新token请求,获取最新的token并进行覆盖。在刷新token的过程中,其他请求会被拦截并等待token刷新完成后再继续发送。当token刷新完成后,isRefreshing会被设置为false,其他请求继续发送。以下是一个示例代码: ```javascript import axios from 'axios' let isRefreshing = false axios.interceptors.response.use( response => { if (response.status === 401) { if (!isRefreshing) { isRefreshing = true return refreshToken().then(res => { const { token } = res.data // 更新token response.headers.Authorization = `${token}` // 继续发送之前被拦截的请求 return axios(response.config) }).catch(err => { // 刷新token失败,跳转到登录页 router.push('/login') return Promise.reject(err) }).finally(() => { isRefreshing = false }) } } return response && response.data }, error => { // 处理错误 return Promise.reject(error) } ) ``` 在上述代码中,refreshToken()是一个发送刷新token请求的函数,你可以根据你的实际情况进行实现。当token过期时,其他请求会被拦截并等待token刷新完成后再继续发送。这样就实现Vue中的无感刷新token的功能。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [VUE中拦截请求并无感知刷新token](https://blog.csdn.net/Wangyuan_wo/article/details/121209540)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Vue 无感刷新token](https://blog.csdn.net/weixin_45308405/article/details/127643153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [VUE前端实现token的无感刷新,即refresh_token](https://blog.csdn.net/yu1431/article/details/130835868)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值