提示
在正式的开发中,Token 处理往往伴随着 路由导航守卫 的使用。
# 一、封装工具
# 1、cookies 方式
- utils / cache.js
import Cookies from 'js-cookie'
const TokenKey = 'lencamo'
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
# 2、localStorage 方式
提示
关闭浏览器 sessionStroage 会被清除,但 localStroage 不会
- utils / cache.js
const localStorage = window.localStorage
export default {
set(key, value) {
localStorage.setItem(key, JSON.stringify(value))
},
get(key) {
return JSON.parse(localStorage.getItem(key)) || null
},
remove(key) {
localStorage.removeItem(key)
},
clear() {
localStorage.clear()
}
}
# 3、storage 综合封装
我们可以使用 class 类 和 enum 枚举实现综合封装。
enum CacheType {
Local,
Session
}
class Cache {
storage: Storage
constructor(storageType: CacheType) {
this.storage = storageType === CacheType.Local ? localStorage : sessionStorage
}
setCache(key: string, value: any) {
this.storage.setItem(key, JSON.stringify(value))
}
getCache(key: string) {
const value = this.storage.getItem(key)
return value ? JSON.parse(value) : null
}
removeCache(key: string) {
this.storage.removeItem(key)
}
clearCache() {
this.storage.clear()
}
}
const localCache = new Cache(CacheType.Local)
const sessionCache = new Cache(CacheType.Session)
export { localCache, sessionCache }
# 二、使用场景
通常情况下 token 的使用有下面两步:
# 1、持久化存储
- cookies 方式
import { getToken, setToken, removeToken } from '@/utils/auth'
const getDefaultState = () => {
return {
token: getToken() // vuex存储的是cookie中的token
}
}
const state = getDefaultState()
const mutations = {
// 1、token处理
SET_TOKEN: (state, token) => {
state.token = token
// 持久化存储token
setToken(token)
},
REMOVE_TOKEN: (state, token) => {
state.token = ''
removeToken()
}
}
- localStorage
import storage from '@/common/storage'
const state = {
userInfo: storage.get('userInfo'),
token: localStorage.getItem('TOKEN')
}
# 2、token 挂载
- axios 实例上
const RenRequest = axios.create({
baseURL: '',
headers: {
Authorization: `Bearer ${getToken}`
}
})
- axios 拦截器上
request.interceptors.request.use((config) => {
const token = store.getters.token
if (token) {
config.headers['X-token'] = `Bearer ${token}`
}
return config
})
# 三、无感 token 刷新
常用于 OAuth2.0 授权登录中,因为这种模式下通常会有两个 token:access_token
、refresh_token
oAuth2.0 中
access_token
默认有效时长为 12 个小时,refresh_token
默认时长为 30 天。在实际运用中需要根据需求设置有效时长。
# 1、refresh 接口
let promise // 节流
export async function refreshTokenApi() {
if (promise) {
return promise
}
promise = new Promise(async (resolve) => {
const response = await request.get('/refresh_token', {
header: {
Authorization: `Bearer ${getRefreshToken()}`
},
__isRefreshTokenAPI: true
})
return response.code === 0
})
promise.finally(() => {
promise = null
})
}
export function isRefreshTokenAPI(config) {
return !!config.__isRefreshTokenAPI
}
# 2、逻辑处理
const RenRequest = axios.create({
baseURL: '',
headers: {
Authorization: `Bearer ${getToken()}`
}
})
// 响应式拦截器
request.interceptors.response.use(
async (response) => {
// 1、存储token、refresh_token
if (response.headers.authorization) {
const token = response.headers.authorization.replace('Bearer', '')
setToken(token)
RenRequest.default.headers.Authorization = `Bearer ${token}`
}
if (response.headers.refresh_token) {
const refresh_token = response.headers.refresh_token.replace('Bearer', '')
setRefreshToken(refresh_token)
}
// 2、token过期处理
// 前提:出现无权限401、排除refreshTokenApi请求
if (response.headers.code === 401 && !isRefreshTokenAPI(response.config)) {
const isSuccess = await refreshTokenApi() // 获取新的token
// refresh_token是否过期
if (isSuccess) {
RenRequest.default.headers.Authorization = `Bearer ${getToken()}` // 使用新的token
const res = await RenRequest.request(res.config)
return resp
} else {
// 跳转到登录页
}
}
return response.data
},
(error) => {
return error
}
)