Vue3 登錄狀態(tài)持久化方案詳解(Token 是什么)
Vue3中登錄狀態(tài)刷新后消失的原因在于內存數(shù)據(jù)重置。
解決方案包括:
- 使用localStorage存儲token和用戶信息;
- 通過Pinia持久化插件自動保存狀態(tài);
- 組合式API封裝認證邏輯;
- 配合路由守衛(wèi)控制訪問權限;
- 使用Cookie支持SSR。
最佳實踐建議采用雙重存儲策略(Vuex+localStorage+sessionStorage),并注意安全性和自動刷新token機制。
根據(jù)項目需求(SPA/SSR/安全級別)選擇合適的持久化方案。
token 通常是 JWT(JSON Web Token)格式的字符串
Vue3登錄狀態(tài)持久化方案
為什么刷新后登錄狀態(tài)會消失?
在 Vue3 項目中,登錄狀態(tài)通常保存在內存中(如 Vuex/Pinia 的狀態(tài)、組件的響應式數(shù)據(jù))。當頁面刷新時:
- 內存數(shù)據(jù)被清空 - 瀏覽器重新加載頁面,JavaScript 環(huán)境重置
- Vue 應用重新初始化 - store 和組件狀態(tài)恢復為初始值
- HTTP 請求是無狀態(tài)的 - 服務器不會自動記住客戶端狀態(tài)
如何持久化登錄狀態(tài)
方案一: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 狀態(tài)管理
userStore.setUser(response.data.user)
userStore.setToken(response.data.token)
}
}
// 2. 應用初始化時恢復狀態(tài)
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')
}
// 初始化檢查登錄狀態(tài)
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
}
}方案四:配合路由守衛(wèi)
// 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
})
// 路由守衛(wèi)
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 + 服務端狀態(tài)同步
- 高安全要求:短期 sessionStorage + 定期刷新 Token
簡要說明:Token 是什么?
Token(令牌) 是一個用于身份驗證和授權的字符串憑證,類似現(xiàn)實生活中的“門禁卡”或“鑰匙”。
核心特點
令牌形式:通常是 JWT(JSON Web Token)格式的字符串
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsImlhdCI6MTYxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
無狀態(tài):服務器不需要存儲 session,自身包含用戶信息
自包含:Token 本身包含了用戶身份信息和過期時間
Token 的工作流程(以登錄為例)
用戶登錄 → 服務器驗證 → 生成Token → 返回給客戶端
↓
客戶端存儲Token → 后續(xù)請求攜帶Token → 服務器驗證Token → 返回數(shù)據(jù)Token 的結構(JWT 為例)
一個典型的 Token 包含三部分:
頭部.載荷.簽名
示例解析:
// 1. 頭部(Header)- 說明令牌類型和算法
{
"alg": "HS256", // 簽名算法
"typ": "JWT" // 令牌類型
}
// 2. 載荷(Payload)- 包含實際數(shù)據(jù)(用戶信息等)
{
"userId": "12345",
"username": "張三",
"exp": 1640995200 // 過期時間戳
}
// 3. 簽名(Signature)- 用于驗證令牌真?zhèn)?
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)為什么使用 Token?
優(yōu)點:
- ? 無狀態(tài):服務器不需要維護 session 存儲
- ? 跨域友好:適合前后端分離和 API 調用
- ? 移動端兼容:適合 App、小程序等
- ? 可擴展性:容易實現(xiàn)分布式系統(tǒng)
缺點:
- ? Token 泄露風險:一旦被盜用,攻擊者可以冒充用戶
- ? 無法立即失效:需要額外的黑名單機制
- ? 存儲安全問題:需要安全的存儲方式(localStorage 有 XSS 風險)
Token vs Cookie
| 特性 | Token | Cookie |
|---|---|---|
| 存儲位置 | localStorage / 內存 | 瀏覽器自動管理 |
| 跨域請求 | 手動添加到 Header | 自動攜帶 |
| 安全性 | 易受 XSS 攻擊 | 易受 CSRF 攻擊 |
| 移動端支持 | 好 | 有限 |
實際應用示例
// 客戶端:存儲和發(fā)送 Token
localStorage.setItem('token', 'your-jwt-token')
// 發(fā)送請求時攜帶 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(() => {
//持久化登錄狀態(tài)
//判斷用戶是否登錄
if (!currentUser.value.isLogin && token.value) {
//如果沒登錄,但是有token
//設置axios的通用頭
axios.defaults.headers.common['Authorization'] = `Bearer ${token.value}`
//獲取用戶信息
store.dispatch('fetchCurrentUser')
}
})
</script>一句話總結:Token 是一種數(shù)字鑰匙,客戶端持有它來證明自己的身份,服務器通過驗證這把鑰匙的真?zhèn)蝸頉Q定是否提供服務。
到此這篇關于Vue3 登錄狀態(tài)持久化方案詳解(Token 是什么)的文章就介紹到這了,更多相關Vue登錄狀態(tài)持久化內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
詳解從vue-loader源碼分析CSS Scoped的實現(xiàn)
這篇文章主要介紹了詳解從vue-loader源碼分析CSS Scoped的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-09-09
vue使用Google Recaptcha驗證的實現(xiàn)示例
我們最近的項目中需要使用谷歌機器人驗證,所以就動手實現(xiàn)一下,本文就來詳細的介紹一下vue Google Recaptcha驗證,感興趣的可以了解一下2021-08-08
Vue+Node實現(xiàn)大文件上傳和斷點續(xù)傳
文件上傳在很多項目中都用的到,如果是幾M的很快就傳送完畢,如果是大文件呢?本文將利用Vue+Node實現(xiàn)大文件上傳和斷點續(xù)傳,感興趣的可以了解一下2022-04-04

