使用axios结合access_token和refresh_token进行无感刷新
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import axios, { type InternalAxiosRequestConfig } from 'axios'
import { ref } from 'vue'
let isRefreshing = false // 标志是否正在刷新token
let requestQueue: { config: InternalAxiosRequestConfig; resolve: any }[] = [] // 存放token失效的请求配置的promise的resolve方法
axios.interceptors.request.use((config) => {
config.headers.Authorization = 'bearer ' + localStorage.getItem('access_token') || '' //拦截器添加token请求头
return config
})
axios.interceptors.response.use(
(res) => {
return res
},
async (err) => {
const { config, response } = err
if (response.status == 401 && !config.url.endsWith('/login')) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
requestQueue.push({ config, resolve }) // accessToken失效后并发请求时,都会失败,把所有的config存起来,刷新完token再请求
if (isRefreshing) {
return
}
isRefreshing = true
await refreshToken() // 使用refreshToken拿到新的有效的accessToken和refreshToken,存放到loalStorage中
isRefreshing = false
for (const { resolve, config } of requestQueue) {
resolve(axios(config)) //这里请求失败拿到请求的config,才能重发请求
}
})
}
return Promise.reject(err)
}
)
const refreshToken = async () => {
const { data } = await axios.get('http://localhost:3000/refreshToken', {
params: {
refresh_token: localStorage.getItem('refresh_token')
}
})
if (data.accessToken && data.refreshToken) {
localStorage.setItem('access_token', data.accessToken)
localStorage.setItem('refresh_token', data.refreshToken)
}
}
const testData1 = ref()
const testData2 = ref()
const query = async () => {
axios.get('http://localhost:3000/test1').then((res) => {
testData1.value = res.data
})
axios.get('http://localhost:3000/test2').then((res) => {
testData2.value = res.data
})
}
const login = async () => {
const { data } = await axios.get('http://localhost:3000/login')
localStorage.setItem('access_token', data.accessToken)
localStorage.setItem('refresh_token', data.refreshToken)
}
</script>
<template>
<button @click="login">登录</button>
<button @click="query()">请求</button>
<div>
<h1>{{ testData1 }}</h1>
<h1>{{ testData2 }}</h1>
</div>
</template>
<style scoped></style>