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

如何通过闭包存储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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 06:54:07