Quasar SSR刷新后Pinia持久化Token服务端无法访问的解决
问题原因
- 服务端无浏览器存储环境:
pinia-plugin-persistedstate默认将状态存在localStorage/sessionStorage中,但SSR服务端运行时没有浏览器的存储API,无法读取这些持久化数据,导致服务端初始化的authStore为空。 - SSR渲染时序问题:服务端渲染阶段会优先发起接口请求,此时客户端还未将持久化的状态同步到服务端的Pinia实例中,请求自然无法携带Token。
解决办法
核心思路是用Cookie作为服务端和客户端的状态桥梁,因为服务端可以通过请求头直接读取Cookie,从而在SSR阶段初始化Pinia状态。
1. 修改Pinia持久化配置,同步状态到Cookie
调整auth store的持久化策略,让状态同时存入localStorage(客户端)和Cookie(服务端可读取):
// stores/auth.js import { defineStore } from 'pinia' import { parseCookie } from 'quasar' export const useAuthStore = defineStore('auth', { state: () => ({ user: null // 包含Token的用户数据 }), persist: { key: 'auth', storage: { getItem: (key) => { // 服务端从Cookie读取,客户端从localStorage读取 if (process.server) { const cookies = parseCookie(useRequestHeaders(['cookie']).cookie || '') return cookies[key] } return localStorage.getItem(key) }, setItem: (key, value) => { localStorage.setItem(key, value) // 客户端写入Cookie,让服务端能获取 if (process.client) { document.cookie = `${key}=${value}; path=/; max-age=86400; SameSite=Lax` // 若不需要客户端读取Token,可添加httpOnly增强安全性:httpOnly=true } }, removeItem: (key) => { localStorage.removeItem(key) if (process.client) { document.cookie = `${key}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT` } } } }, // ...其他actions、getters逻辑 })
2. 服务端初始化Pinia状态
在Quasar的SSR入口文件src/ssr/server-entry.js中,解析请求Cookie并预加载authStore状态:
// src/ssr/server-entry.js import { parseCookie } from 'quasar' import { createPinia } from 'pinia' import { useAuthStore } from '../stores/auth' export default async ({ app, ssrContext }) => { const pinia = createPinia() app.use(pinia) // 服务端从Cookie初始化authStore if (ssrContext?.req?.headers?.cookie) { const cookies = parseCookie(ssrContext.req.headers.cookie) const authStore = useAuthStore(pinia) if (cookies.auth) { try { authStore.user = JSON.parse(cookies.auth) } catch (e) { // 解析失败则清空状态 authStore.user = null } } } // ...其他SSR初始化逻辑 }
3. 调整Axios请求拦截器
确保服务端和客户端都能正确获取Token并设置请求头:
// src/boot/axios.js import { useAuthStore } from 'stores/auth' import { parseCookie } from 'quasar' export default ({ app, ssrContext }) => { const axios = app.config.globalProperties.$axios axios.interceptors.request.use((config) => { let authStore if (process.server) { // 服务端通过pinia实例获取store,同时从Cookie同步状态 const pinia = app.config.globalProperties.$pinia authStore = useAuthStore(pinia) if (ssrContext?.req?.headers?.cookie) { const cookies = parseCookie(ssrContext.req.headers.cookie) if (cookies.auth) { try { authStore.user = JSON.parse(cookies.auth) } catch (e) {} } } } else { // 客户端直接获取store authStore = useAuthStore() } // 设置请求头Token if (authStore.user?.token) { config.headers.Authorization = `Bearer ${authStore.user.token}` } return config }) }
额外注意事项
- 如果不需要客户端直接操作Token,建议将Cookie设置为
httpOnly,避免XSS攻击风险,但此时客户端无法通过JS读取Token,需确保所有Token相关操作都在服务端完成。 - 务必对Cookie中的JSON数据做异常捕获,防止解析失败导致状态异常。
内容的提问来源于stack exchange,提问作者morteza mortezaie
相关产品推荐
相关产品推荐

