You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quasar SSR刷新后Pinia持久化Token服务端无法访问的解决

问题原因
  1. 服务端无浏览器存储环境:pinia-plugin-persistedstate默认将状态存在localStorage/sessionStorage中,但SSR服务端运行时没有浏览器的存储API,无法读取这些持久化数据,导致服务端初始化的authStore为空。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 13:10:02