双令牌
// router.js
router.beforeEach(async (to, from, next) => {
NProgress.start()
const token = getToken()
if (to.path === '/login') {
// 如果是访问登录界面,清空用户信息
store.commit('logout')
next()
} else if (whiteList.includes(to.path)) {
if (token && !store.state.uid) {
await store.dispatch('getUserInfo')
}
next()
} else {
// 如果浏览器缓存中有用户信息,则表明已登录
// 如果访问非登录界面,且户会话信息不存在,代表未登录,则跳转到登录界面
if (!token) {
next(`/login?redirect=${to.path}`)
} else {
if (store.state.uid) {
next()
} else {
try {
await store.dispatch('getUserInfo')
next()
} catch {
// 清除数据,跳转到登录界面
store.commit('logout')
next(`/login?redirect=${to.path}`)
}
}
}
}
})
// utils/request.ts
import axios from 'axios'
import { ElMessage } from 'element-plus'
import type { AxiosRequestConfig } from 'axios'
import router from '@/router'
import store from '@/store'
import { getRefreshToken, getToken, setRefreshToken, setToken } from '@/utils/tools'
import { refreshToken } from '@/apis'
export interface ISuccessData<T> {
code: number;
message: string;
data: T
}
// 创建axios实例
const service = axios.create({
baseURL: '/',
timeout: 20000,
headers: { 'Content-Type': 'application/json;charset=utf-8' }
})
service.interceptors.request.use(
config => {
const token = getToken()
if (token) {
config.headers = { token }
}
return config
}
)
service.interceptors.response.use(
async res => {
if (res.status === 200 && res.data) {
// 拦截指定错误码 code=401 刷新令牌
if (res.data.code && res.data.code === 401) {
if (getRefreshToken()) {
if (await refresh()) {
// 重发当前请求
const config = res.config
const reRes = service(config)
return reRes
}
}
ElMessage.error('登录失效,请重新登录')
setTimeout(() => {
store.commit('logout')
router.push('/login')
}, 1000)
} else if (res.data.code && res.data.code !== 200 && res.data.message) {
ElMessage.error(res.data.message)
}
} else {
ElMessage.error('服务器错误,请稍后再试')
}
return res.data
},
err => {
ElMessage.error('服务器错误,请稍后再试')
return Promise.reject(err)
}
)
async function refresh () {
const code = getRefreshToken()
if (code) {
const res = await refreshToken(code)
if (res.code === 200) {
setToken(res.data.token)
setRefreshToken(res.data.refreshToken)
return res.data.token
} else {
return false
}
} else {
return false
}
}
export class Request {
static get<T> (url: string, params?: object): Promise<ISuccessData<T>> {
return service.get(url, { params })
}
static post<T> (
url: string,
data?: object,
config?: AxiosRequestConfig
): Promise<ISuccessData<T>> {
return service.post(url, data, config)
}
static put<T> (
url: string,
data?: object,
config?: AxiosRequestConfig
): Promise<ISuccessData<T>> {
return service.put(url, data, config)
}
static patch<T> (
url: string,
data?: object,
config?: AxiosRequestConfig
): Promise<ISuccessData<T>> {
return service.patch(url, data, config)
}
static delete<T> (url: string, data?: object): Promise<ISuccessData<T>> {
return service.delete(url, { data })
}
}
// utils/tools.ts
const TOKEN_KEY = 'token'
const REFRESH_TOKEN_KEY = 'refresh-token'
export function getToken () {
return localStorage.getItem(TOKEN_KEY)
}
export function setToken (token: string) {
localStorage.setItem(TOKEN_KEY, token)
}
export function removeToken () {
localStorage.removeItem(TOKEN_KEY)
}
export function getRefreshToken () {
return localStorage.getItem(REFRESH_TOKEN_KEY)
}
export function setRefreshToken (token: string) {
localStorage.setItem(REFRESH_TOKEN_KEY, token)
}
export function removeRefreshToken () {
localStorage.removeItem(REFRESH_TOKEN_KEY)
}