一、引言:为什么Token管理是前端核心能力

- 现代 Web 应用的认证架构演进 :Session -> JWT -> OAuth2.0
- 前端 Token 管理的三大挑战: 安全性、无感刷新、状态同步
二、双 Token 机制 :安全架构
1、令牌分工原理
json
// 令牌结构示例
{
"access_token": "eyJhbG...", // 短期令牌(30分钟)
"refresh_token": "f7a2e...", // 长期令牌(7天)
"expires_in": 1800 // 过期时间
}
- access_token:高频使用,携带用户身份,必须短时效
- refresh_token:仅用于刷新,绝不出现在常规请求中
2.安全存储方案对比
存储位置 | 适用令牌类型 | 安全等级 | 生命周期 | 风险 | |
---|---|---|---|---|---|
sessionStorage | access_token | ★★★★☆ | 标签页关闭时失效 | XSS 可读取 | |
内存变量 | refresh_token | ★★★★★ | 页面刷新即丢失 | 无法持久化 | |
HttpOnly Cookie | refresh_token | ★★★★★ | 可设长时效 | 需防 CSRF | |
localStorage | 不推荐 | ★☆☆☆☆ | 永久存储 | XSS 完全暴露 |
💡 黄金法则:Access Token 存易失位置,Refresh Token 用 HttpOnly Cookie
3、无感刷新实战:Axios 拦截器深度优化
javascript
// 请求拦截器
service.interceptors.request.use(
(config) => {
try {
// 从sessionStorage获取access_token
const token = sessionStorage.getItem('access_token')
if (token) {
config.headers.Authorization = `${token}`
}
return config
} catch (error) {
console.error('获取token失败:', error)
return config
}
},
(error) => {
return Promise.reject(error)
},
)
javascript
// 响应拦截器
service.interceptors.response.use(
(response) => {
return response
},
async (error) => {
// 获取错误响应状态
const { response } = error
if (response?.status === 401) {
// token过期
const config = error.config
if (!isRefreshing) {
isRefreshing = true
try {
// 尝试刷新token
const userStore = useUserStore()
const refreshResult = await userStore.refreshToken()
if (refreshResult) {
// 重新设置token
const newToken = sessionStorage.getItem('access_token')
config.headers.Authorization = newToken
// 重新请求队列中的请求
requests.forEach((cb) => cb(newToken))
requests = []
// 重试当前请求
return service(config)
}
} catch (err) {
console.error('刷新token失败:', err)
// 刷新失败,清空token并跳转到登录页
const userStore = useUserStore()
userStore.clearUserInfo()
window.location.href = '/login'
return Promise.reject(err)
} finally {
isRefreshing = false
}
} else {
// 正在刷新token,将请求加入队列
return new Promise((resolve) => {
requests.push((token) => {
config.headers.Authorization = `${token}`
resolve(service(config))
})
})
}
}
return Promise.reject(error)
},
三、用户信息pinia持久化
做用户功能时,刷新一下页面用户信息就没了,我是用pinia持久化的,然后发现是没有下载插件和配置pinia-plugin-persistedstate。
javascript
{
persist: {
// 存储在 sessionStorage 中的键名
key: 'user-store',
// 使用 sessionStorage 作为存储方式
storage: window.sessionStorage,
// 只持久化 store 中的 userInfo 数据
paths: ['userInfo']
}
}
这个配置的作用是:
1.持久化存储:
- 当页面刷新时,Pinia store 中的数据会被重置
- 通过这个配置,指定的数据会自动保存到 sessionStorage 中
- 页面刷新后,数据会自动从 sessionStorage 恢复到 store 中
2.选择性持久化:
- paths: ['userInfo'] 表示只持久化 store 中的 userInfo 数据
- 其他数据如 tokenInfo 不会被持久化
- 这样可以更精确地控制哪些数据需要保存
3.使用 sessionStorage:
- 数据存储在 sessionStorage 中,而不是 localStorage
- 这意味着数据只在当前会话期间有效
- 关闭浏览器后数据会被清除,保证了安全性
举个例子:
arduino
// 用户登录后,userInfo 会被自动保存到 sessionStorage
userInfo.value = {
username: 'test',
email: '[email protected]'
}
// 页面刷新后,userInfo 会自动从 sessionStorage 恢复
// 不需要手动处理恢复逻辑
主要好处:
- 避免页面刷新后用户信息丢失
- 不需要手动管理数据的存储和恢复
- 通过 sessionStorage 确保了数据的安全性
四、架构图解:
