news 2026/10/31 6:16:23

Vue3 登录状态持久化方案(附:Token 是什么)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue3 登录状态持久化方案(附:Token 是什么)

Vue3中登录状态刷新后消失的原因在于内存数据重置。


解决方案包括:

  1. 使用localStorage存储token和用户信息;
  2. 通过Pinia持久化插件自动保存状态;
  3. 组合式API封装认证逻辑;
  4. 配合路由守卫控制访问权限;
  5. 使用Cookie支持SSR。

最佳实践建议采用双重存储策略(Vuex+localStorage+sessionStorage),并注意安全性和自动刷新token机制。


根据项目需求(SPA/SSR/安全级别)选择合适的持久化方案。


token 通常是 JWT(JSON Web Token)格式的字符串


Vue3登录状态持久化方案


为什么刷新后登录状态会消失?

在 Vue3 项目中,登录状态通常保存在内存中(如 Vuex/Pinia 的状态、组件的响应式数据)。当页面刷新时:

  1. 内存数据被清空- 浏览器重新加载页面,JavaScript 环境重置

  2. Vue 应用重新初始化- store 和组件状态恢复为初始值

  3. 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')

最佳实践建议

  1. 双重存储策略:

    // 同时使用多种存储方式 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 }) }
  2. 安全考虑:

    • 对于敏感信息,考虑使用sessionStorage(标签页关闭即清除)

    • 或者使用加密存储:npm install crypto-js

    import CryptoJS from 'crypto-js' const encryptData = (data) => { return CryptoJS.AES.encrypt( JSON.stringify(data), 'your-secret-key' ).toString() }
  3. 自动刷新 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(令牌)是一个用于身份验证和授权的字符串凭证,类似现实生活中的“门禁卡”或“钥匙”。


核心特点

  1. 令牌形式:通常是 JWT(JSON Web Token)格式的字符串

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsImlhdCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  2. 无状态:服务器不需要存储 session,自身包含用户信息

  3. 自包含: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

特性TokenCookie
存储位置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 是一种数字钥匙,客户端持有它来证明自己的身份,服务器通过验证这把钥匙的真伪来决定是否提供服务。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/31 6:15:42

【计算机毕业设计案例】基于ssm的铜业网络采购批发平台铜产品商城管理系统(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/10/31 3:54:26

一文读懂算法全体系:核心大类、落地行业与发展趋势深度解析

在数字化、智能化全面渗透的当下&#xff0c;算法早已不是计算机领域的小众概念&#xff0c;而是驱动互联网、金融、制造、医疗等全行业升级的核心引擎。从基础程序运行到 AI 大模型生成内容&#xff0c;从电商精准推荐到自动驾驶路径规划&#xff0c;背后都是不同类型算法在协…

作者头像 李华
网站建设 2026/10/28 14:38:36

Thinkphp和Laravel框架的基于建筑物识别的无人驾驶车辆路径规划系统

目录 ThinkPHP 与 Laravel 框架在无人驾驶路径规划系统的应用建筑物识别技术整合路径规划算法实现数据存储与实时通信性能优化与扩展性安全性与错误处理 开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; ThinkPHP 与 Laravel 框…

作者头像 李华
网站建设 2026/10/26 8:34:15

Thinkphp和Laravel框架的家庭光伏发电系统能源采集管理系统

目录ThinkPHP与Laravel框架在家庭光伏发电系统能源采集管理系统的应用开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;ThinkPHP与Laravel框架在家庭光伏发电系统能源采集管理系统的应用 家庭光伏发电系统能源采集管理系统需要高…

作者头像 李华
网站建设 2026/10/26 7:57:26

学霸同款8个降AIGC平台 千笔·降AIGC助手帮你降AI率

AI降重工具的崛起&#xff0c;为论文写作带来新希望 随着人工智能技术的飞速发展&#xff0c;AI写作工具在学术领域的应用越来越广泛。然而&#xff0c;随之而来的AIGC率问题也成为了许多学生尤其是专科生的头疼难题。如何在保持论文原创性的同时&#xff0c;有效降低AI痕迹和查…

作者头像 李华