vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue登录状态持久化

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

作者:前端开发_穆金秋

本文给大家介绍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 的状态、组件的响应式数据)。当页面刷新时:

如何持久化登录状态

方案一: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()
}

自动刷新 Token

// 定期刷新 token
setInterval(async () => {
  if (isAuthenticated()) {
    const newToken = await api.refreshToken()
    localStorage.setItem('token', newToken)
  }
}, 30 * 60 * 1000) // 每30分钟

选择哪种方案取决于你的具体需求:

简要说明: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?

优点:

缺点:

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

到此这篇关于Vue3 登录状态持久化方案详解(Token 是什么)的文章就介绍到这了,更多相关Vue登录状态持久化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文