Nuxt3中如何在Pinia Store配置Cookie并监听过期刷新令牌
一、替换存储方式为Cookie
原代码用useStorage默认存在localStorage,要改成Cookie并设置过期时间,推荐用Nuxt3内置的useCookie(和@vueuse/core的useCookie用法兼容)。修改Pinia的state定义:
import { defineStore } from "pinia"; import repo from "~/repositories/index"; import { getUserImage } from "~/utils/helperFunctions"; import { useCookie } from "#app"; // 引入Nuxt内置Cookie工具 export const useUserStore = defineStore("user-store", { state: () => ({ email: useCookie("email", { default: "", maxAge: 0 }), img_url: useCookie("img_url", { default: "", maxAge: 0 }), refreshToken: useCookie("refreshToken", { default: "", maxAge: 86400 * 7 }), // 刷新Token有效期设为7天(按需调整) bearerToken: useCookie("bearerToken", { default: "", maxAge: 0 }), expiryIn: useCookie("expiryIn", { default: 0, maxAge: 0 }), domains: useCookie("domains", { default: [], maxAge: 0 }), refreshTimer: null as NodeJS.Timeout | null, // 存储定时器ID,用于后续清除 }),
二、新增Token刷新Action
在actions中添加refreshToken方法,负责调用后端接口获取新Token并更新状态:
async refreshToken() { try { if (!this.refreshToken) return; const data = await repo.user.refreshToken(this.refreshToken); if (data) { // 更新Token和有效期 this.bearerToken = data.bearerToken; this.expiryIn = data.expiryIn; // 同步更新Cookie的过期时间 this.bearerToken.options.maxAge = data.expiryIn; this.expiryIn.options.maxAge = data.expiryIn; // 重新设置刷新定时器 this.setRefreshTimer(); } else { // 刷新失败,执行登出 this.logOut(); } } catch (error) { console.error("[UserStore] [RefreshToken] 刷新失败:", error); this.logOut(); } },
三、登录后设置过期定时器
登录成功后,根据返回的expiryIn(秒数)设置定时器,提前30秒触发刷新(避免刚好过期时请求失败):
async login(email: string, password: string) { try { const data = await repo.user.login(email, password); if (data != null) { // 原有状态赋值逻辑保留 this.$state.email = email; this.$state.img_url = getUserImage(email); this.$state.bearerToken = data.bearerToken; this.$state.expiryIn = data.expiryIn; this.$state.refreshToken = data.refreshToken; this.$state.domains = data.domains; // 同步设置所有用户相关Cookie的过期时间 const cookieMaxAge = data.expiryIn; this.email.options.maxAge = cookieMaxAge; this.img_url.options.maxAge = cookieMaxAge; this.bearerToken.options.maxAge = cookieMaxAge; this.expiryIn.options.maxAge = cookieMaxAge; this.domains.options.maxAge = cookieMaxAge; // 设置刷新定时器 this.setRefreshTimer(); return true; } else { console.log("[UserStore] [Login] 登录失败"); return false; } } catch (error) { console.log("[UserStore] [Login] 错误:", error); return false; } }, // 封装定时器设置逻辑 setRefreshTimer() { // 清除已有定时器,避免重复触发 if (this.refreshTimer) { clearTimeout(this.refreshTimer); } // 计算延迟时间:提前30秒刷新,转毫秒单位 const refreshDelay = (this.expiryIn - 30) * 1000; if (refreshDelay > 0) { this.refreshTimer = setTimeout(() => { this.refreshToken(); }, refreshDelay); } },
四、页面加载时检查Token状态
在Nuxt插件中初始化Store时,检查Token是否即将过期,自动触发刷新:
在plugins/user-store.ts中添加:
import { useUserStore } from "~/stores/user-store"; export default defineNuxtPlugin((nuxtApp) => { const userStore = useUserStore(nuxtApp.$pinia); if (userStore.bearerToken && userStore.expiryIn) { // 计算剩余有效时间(需确保expiryIn是剩余秒数,若后端返回的是过期时间戳,需转换为剩余时间) const remainingTime = userStore.expiryIn; if (remainingTime < 30) { // 剩余时间不足30秒,直接刷新 userStore.refreshToken(); } else { // 设置定时器 userStore.setRefreshTimer(); } } });
五、登出时清除定时器
修改logOut方法,清除定时器避免内存泄漏:
logOut() { console.log("[UserStore] [LogOut] 正在退出"); // 清除刷新定时器 if (this.refreshTimer) { clearTimeout(this.refreshTimer); this.refreshTimer = null; } // 原有状态清空逻辑保留 this.$state.email = ""; this.$state.img_url = ""; this.$state.bearerToken = ""; this.$state.expiryIn = 0; this.$state.refreshToken = ""; this.$state.domains = []; },
关键注意事项
- 后端返回的
expiryIn必须是秒数,因为Cookie的maxAge参数单位为秒。 - 若后端返回的是Token过期时间戳(Unix时间),需转换为剩余时间:
expiryTimestamp - Math.floor(Date.now() / 1000)。 - 刷新Token的API需接受
refreshToken作为参数,返回新的bearerToken和expiryIn。
内容的提问来源于stack exchange,提问作者Abdullah Sheikh
相关产品推荐
相关产品推荐

