如何通过闭包存储JWT的access_token以替代localStorage存储方案
闭包存储access_token实现方案
核心实现逻辑
利用立即执行函数(IIFE)创建私有作用域,将access_token作为私有变量存储在闭包内,仅对外暴露受控的读写、清除方法,避免token暴露到全局上下文或本地存储中,降低XSS窃取风险。
步骤1:封装闭包存储模块
新建src/utils/auth.js文件,实现闭包存储逻辑:
// 闭包形式的access_token存储器 const accessTokenStorage = (() => { // 私有变量,仅内部可访问,不会持久化到本地 let _accessToken = null return { // 获取access_token get: () => _accessToken, // 存储access_token set: (token) => { _accessToken = token }, // 清空access_token clear: () => { _accessToken = null } } })() export default accessTokenStorage
步骤2:修改Vuex授权逻辑
调整Vuex的auth模块,适配新的存储方式,原有isLoggedIn getter不需要改动:
import accessTokenStorage from '@/utils/auth' const state = { // 初始化从闭包中读取token,页面刷新后默认为null token: accessTokenStorage.get() } const getters = { // 保持原有判断逻辑不变 isLoggedIn: state => !!state.token } const mutations = { SET_TOKEN(state, token) { // 同步更新闭包存储和Vuex响应式状态 accessTokenStorage.set(token) state.token = token }, CLEAR_TOKEN(state) { accessTokenStorage.clear() state.token = null } } const actions = { // 登录成功后调用SET_TOKEN存储返回的access_token async login({ commit }, loginParams) { const { data } = await loginApi(loginParams) commit('SET_TOKEN', data.access_token) }, // 退出登录时清空token async logout({ commit }) { await logoutApi() commit('CLEAR_TOKEN') }, // 新增刷新access_token方法,页面刷新后调用 async refreshToken({ commit }) { try { // 请求会自动携带HttpOnly类型的refresh_token cookie const { data } = await refreshTokenApi() commit('SET_TOKEN', data.access_token) return Promise.resolve() } catch (err) { // refresh_token过期时清空状态跳转登录 commit('CLEAR_TOKEN') window.location.href = '/login' return Promise.reject(err) } } }
步骤3:兼容页面刷新场景
闭包存储的token仅存在应用内存中,页面刷新后会丢失,需要在应用初始化或路由守卫中增加刷新逻辑:
// 路由守卫示例 router.beforeEach(async (to, from, next) => { const needLogin = to.meta.requireAuth const isLoggedIn = store.getters.isLoggedIn if (needLogin && !isLoggedIn) { try { // 尝试用refresh_token换取新的access_token await store.dispatch('refreshToken') next() } catch (err) { next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) } return } next() })
步骤4:替换原有存储逻辑
- 移除所有读写localStorage中access_token的代码
- 请求拦截器中从闭包存储获取token挂载请求头:
import accessTokenStorage from '@/utils/auth' axios.interceptors.request.use(config => { const token = accessTokenStorage.get() if (token) { config.headers.Authorization = `Bearer ${token}` } return config })
注意事项
如果你的项目需要支持多标签页共享登录状态,该方案默认不支持跨标签页同步,因为内存是单标签页隔离的,需要额外通过storage事件监听配合临时localStorage传递新token实现,无相关需求可忽略。
内容的提问来源于stack exchange,提问作者user17056867
相关产品推荐
相关产品推荐

