news 2026/2/10 14:52:44

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/2/6 4:55:06

学霸同款8个降AIGC平台 千笔·专业降AI率智能体解决论文查重难题

AI降重工具&#xff1a;论文写作的得力助手 随着人工智能技术在学术领域的广泛应用&#xff0c;越来越多的学生开始使用AI工具进行论文写作。然而&#xff0c;AI生成的内容往往存在明显的痕迹&#xff0c;导致AIGC率偏高&#xff0c;影响论文的原创性和查重结果。为了应对这一问…

作者头像 李华
网站建设 2026/2/10 8:19:22

Python数据分析:英国电商销售数据实战

&#x1f9f9;从脏数据到干净洞察&#xff01;手把手实战英国电商销售数据清洗&#xff08;附完整代码每步输出&#xff09; &#x1f4a1; 新手友好 | 代码全公开 | 每一步都有结果截图式描述 大家好&#xff01;我是你们的数据搭子 &#x1f44b; 今天带来一个超实用的数据分…

作者头像 李华
网站建设 2026/2/7 0:10:31

Qt 技巧笔记 (五) Qt消息框(QMessageBox)的全面使用指南

Qt 技巧笔记 (五) Qt消息框&#xff08;QMessageBox&#xff09;的全面使用指南 ​ 在Qt框架开发中&#xff0c;消息框组件(QMessageBox) 是处理用户交互的核心工具。本笔记系统梳理了QMessageBox的6种预定义类型&#xff0c;静态调用与实例化调用的对比&#xff0c;自…

作者头像 李华
网站建设 2026/2/11 2:56:06

【课程设计/毕业设计】基于SSM的常熟非遗数字化管理系统基于ssm的常熟非遗数字化管理系统【附源码、数据库、万字文档】

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

作者头像 李华