Vue3 登录状态持久化方案详解(Token 是什么)
作者:前端开发_穆金秋
本文给大家介绍Vue3登录状态持久化方案详解包括Token 是什么的相关知识,本文结合实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
Vue3中登录状态刷新后消失的原因在于内存数据重置。
解决方案包括:
- 使用localStorage存储token和用户信息;
- 通过Pinia持久化插件自动保存状态;
- 组合式API封装认证逻辑;
- 配合路由守卫控制访问权限;
- 使用Cookie支持SSR。
最佳实践建议采用双重存储策略(Vuex+localStorage+sessionStorage),并注意安全性和自动刷新token机制。
根据项目需求(SPA/SSR/安全级别)选择合适的持久化方案。
token 通常是 JWT(JSON Web Token)格式的字符串
Vue3登录状态持久化方案
为什么刷新后登录状态会消失?
在 Vue3 项目中,登录状态通常保存在内存中(如 Vuex/Pinia 的状态、组件的响应式数据)。当页面刷新时:
- 内存数据被清空 - 浏览器重新加载页面,JavaScript 环境重置
- Vue 应用重新初始化 - store 和组件状态恢复为初始值
- HTTP 请求是无状态的 - 服务器不会自动记住客户端状态
如何持久化登录状态
方案一:Token + localStorage(最常用)
// 1. 登录成功后保存 token
import { ref } from 'vue'
const login = async () => {
const response = await api.login(username, password)
if (response.success) {
// 保存到 localStorage
localStorage.setItem('token', response.data.token)
localStorage.setItem('userInfo', JSON.stringify(response.data.user))
// 同时保存到 Vue 状态管理
userStore.setUser(response.data.user)
userStore.setToken(response.data.token)
}
}
// 2. 应用初始化时恢复状态
import { onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
onMounted(() => {
// 从 localStorage 恢复
const token = localStorage.getItem('token')
const userInfo = localStorage.getItem('userInfo')
if (token && userInfo) {
userStore.setToken(token)
userStore.setUser(JSON.parse(userInfo))
}
})方案二:使用 Vuex/Pinia 持久化插件
安装 pinia-plugin-persistedstate:
npm install pinia-plugin-persistedstate
配置:
// main.js / main.ts
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(createPersistedState({
storage: localStorage,
// 或使用 sessionStorage
// storage: sessionStorage,
// 自定义序列化(可选)
serializer: {
serialize: JSON.stringify,
deserialize: JSON.parse,
},
// 自动恢复所有 store
auto: true
}))
app.use(pinia)使用:
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
token: '',
userInfo: null
}),
actions: {
login(data) {
this.token = data.token
this.userInfo = data.user
},
logout() {
this.token = ''
this.userInfo = null
}
},
// 启用持久化
persist: {
key: 'user', // 存储的 key
storage: localStorage,
paths: ['token', 'userInfo'] // 只持久化这些字段
}
})方案三:组合式 API 封装
// composables/useAuth.js
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
export function useAuth() {
const token = ref('')
const user = ref(null)
const router = useRouter()
// 登录
const login = async (credentials) => {
const response = await api.login(credentials)
setAuth(response.data)
}
// 设置认证信息
const setAuth = (data) => {
token.value = data.token
user.value = data.user
// 保存到 localStorage
localStorage.setItem('auth_token', data.token)
localStorage.setItem('auth_user', JSON.stringify(data.user))
// 设置 axios 默认请求头
api.setAuthToken(data.token)
}
// 登出
const logout = () => {
token.value = ''
user.value = null
localStorage.removeItem('auth_token')
localStorage.removeItem('auth_user')
api.clearAuthToken()
router.push('/login')
}
// 初始化检查登录状态
const initAuth = () => {
const savedToken = localStorage.getItem('auth_token')
const savedUser = localStorage.getItem('auth_user')
if (savedToken && savedUser) {
token.value = savedToken
user.value = JSON.parse(savedUser)
api.setAuthToken(savedToken)
}
}
// 检查是否已登录
const isAuthenticated = () => {
return !!token.value
}
return {
token,
user,
login,
logout,
initAuth,
isAuthenticated
}
}方案四:配合路由守卫
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/dashboard',
component: () => import('@/views/Dashboard.vue'),
meta: { requiresAuth: true }
},
{
path: '/login',
component: () => import('@/views/Login.vue'),
meta: { guestOnly: true }
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 路由守卫
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token')
// 需要认证的页面
if (to.meta.requiresAuth && !token) {
next('/login')
return
}
// 仅游客可访问的页面(如登录页)
if (to.meta.guestOnly && token) {
next('/dashboard')
return
}
next()
})方案五:使用 Cookie(服务端渲染友好)
// 使用 js-cookie 库
import Cookies from 'js-cookie'
// 保存 token
Cookies.set('token', response.data.token, {
expires: 7, // 7天后过期
secure: true, // HTTPS only
sameSite: 'strict'
})
// 读取 token
const token = Cookies.get('token')最佳实践建议
双重存储策略:
// 同时使用多种存储方式
const saveAuth = (data) => {
// 1. Vuex/Pinia
store.setAuth(data)
// 2. localStorage(主要持久化)
localStorage.setItem('auth', JSON.stringify(data))
// 3. sessionStorage(标签页内有效)
sessionStorage.setItem('auth_temp', JSON.stringify(data))
// 4. Cookie(SSR 需要)
Cookies.set('token', data.token, { expires: 7 })
}安全考虑:
import CryptoJS from 'crypto-js'
const encryptData = (data) => {
return CryptoJS.AES.encrypt(
JSON.stringify(data),
'your-secret-key'
).toString()
}- 对于敏感信息,考虑使用
sessionStorage(标签页关闭即清除) - 或者使用加密存储:
npm install crypto-js
自动刷新 Token:
// 定期刷新 token
setInterval(async () => {
if (isAuthenticated()) {
const newToken = await api.refreshToken()
localStorage.setItem('token', newToken)
}
}, 30 * 60 * 1000) // 每30分钟选择哪种方案取决于你的具体需求:
- 纯 SPA 应用:localStorage + Pinia 持久化插件
- 需要 SSR:Cookie + 服务端状态同步
- 高安全要求:短期 sessionStorage + 定期刷新 Token
简要说明:Token 是什么?
Token(令牌) 是一个用于身份验证和授权的字符串凭证,类似现实生活中的“门禁卡”或“钥匙”。
核心特点
令牌形式:通常是 JWT(JSON Web Token)格式的字符串
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsImlhdCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
无状态:服务器不需要存储 session,自身包含用户信息
自包含:Token 本身包含了用户身份信息和过期时间
Token 的工作流程(以登录为例)
用户登录 → 服务器验证 → 生成Token → 返回给客户端
↓
客户端存储Token → 后续请求携带Token → 服务器验证Token → 返回数据Token 的结构(JWT 为例)
一个典型的 Token 包含三部分:
头部.载荷.签名
示例解析:
// 1. 头部(Header)- 说明令牌类型和算法
{
"alg": "HS256", // 签名算法
"typ": "JWT" // 令牌类型
}
// 2. 载荷(Payload)- 包含实际数据(用户信息等)
{
"userId": "12345",
"username": "张三",
"exp": 1640995200 // 过期时间戳
}
// 3. 签名(Signature)- 用于验证令牌真伪
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)为什么使用 Token?
优点:
- ✅ 无状态:服务器不需要维护 session 存储
- ✅ 跨域友好:适合前后端分离和 API 调用
- ✅ 移动端兼容:适合 App、小程序等
- ✅ 可扩展性:容易实现分布式系统
缺点:
- ❌ Token 泄露风险:一旦被盗用,攻击者可以冒充用户
- ❌ 无法立即失效:需要额外的黑名单机制
- ❌ 存储安全问题:需要安全的存储方式(localStorage 有 XSS 风险)
Token vs Cookie
| 特性 | Token | Cookie |
|---|---|---|
| 存储位置 | localStorage / 内存 | 浏览器自动管理 |
| 跨域请求 | 手动添加到 Header | 自动携带 |
| 安全性 | 易受 XSS 攻击 | 易受 CSRF 攻击 |
| 移动端支持 | 好 | 有限 |
实际应用示例
// 客户端:存储和发送 Token
localStorage.setItem('token', 'your-jwt-token')
// 发送请求时携带 Token
fetch('/api/user', {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`
}
})
// 服务器端:验证 Token
app.get('/api/protected', (req, res) => {
const token = req.headers.authorization?.split(' ')[1]
if (!token) return res.status(401).send('无权限')
// 验证 Token 是否有效
const decoded = jwt.verify(token, 'secret-key')
// 如果有效,处理请求...
})设置axios的通用头
App.vue
<template>
<div class="container">
<Loader v-if="isLoading" text="正在加载😊" background="rgba(0,0,0,0.8)"></Loader>
<global-header :user="currentUser"></global-header>
<router-view></router-view>
<footer class="text-center py-4 text-secondary bg-light mt-6">
<small>
......
</small>
</footer>
</div>
</template>
<script lang="ts" setup>
import 'bootstrap/dist/css/bootstrap.min.css'
import GlobalHeader from './components/GlobalHeader.vue'
import { computed, onMounted } from 'vue'
import { useStore } from 'vuex'
import Loader from './components/Loader.vue'
import axios from 'axios'
const store = useStore()
const currentUser = computed(() => {
return store.state.user
})
const isLoading = computed(() => {
return store.state.loading
})
//获取store中的token
const token = computed(() => {
return store.state.token
})
onMounted(() => {
//持久化登录状态
//判断用户是否登录
if (!currentUser.value.isLogin && token.value) {
//如果没登录,但是有token
//设置axios的通用头
axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`
//获取用户信息
store.dispatch('fetchCurrentUser')
}
})
</script>一句话总结:Token 是一种数字钥匙,客户端持有它来证明自己的身份,服务器通过验证这把钥匙的真伪来决定是否提供服务。
到此这篇关于Vue3 登录状态持久化方案详解(Token 是什么)的文章就介绍到这了,更多相关Vue登录状态持久化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
