当前位置: 首页 > article >正文

确保刷新页面后用户登录状态不会失效,永久化存储用户登录信息

为了解决刷新页面后用户登录失效的问题,我们需要对以下三个文件进行修改:

  1. main.js:在应用初始化时从 localStorage 中恢复 Token。

  2. App.vue:监听路由变化,确保登录状态和动态路由加载正确。

  3. permission.js(路由守卫):优化路由守卫逻辑,确保 Token 和动态路由的正确处理。

1. 修改 main.js

在应用初始化时,从 localStorage 中恢复 Token,并更新到 Vuex 中。

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import SvgIcon from '@/icons';
import '@/router/permission.js'; // 引入路由守卫
import zhCn from 'element-plus/es/locale/lang/zh-cn'; // 国际化中文
import request from "@/util/request";

const app = createApp(App);

// 从 localStorage 中恢复 Token
const token = localStorage.getItem('token');
if (token) {
  store.commit('SET_TOKEN', token); // 更新 Vuex 中的 Token
}

// 注册全局属性
app.config.globalProperties.$request = request; // 挂载请求方法
app.config.globalProperties.$baseUrl = process.env.VUE_APP_BASEURL; // 挂载基础 URL

// 使用插件
SvgIcon(app);
app.use(ElementPlus, {
    locale: zhCn,
});
app.use(store);
app.use(router);

// 挂载应用
app.mount('#app');

修改 App.vue

在 App.vue 中监听路由变化,确保登录状态和动态路由加载正确。

<template>
  <router-view />
</template>

<script setup>
import { watch, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import store from './store';

const route = useRoute();
const whitePath = ['/adminlogin', '/index', '/'];

// 监听路由变化,动态添加标签页
watch(
  route,
  (to) => {
    if (whitePath.indexOf(to.path) === -1) {
      const tab = {
        name: to.name,
        path: to.path
      };
      store.commit("ADD_TABS", tab); // 添加标签页
    }
  },
  { deep: true, immediate: true }
);

// 在组件挂载时检查 Token
onMounted(() => {
  const token = localStorage.getItem('token');
  if (token) {
    store.commit('SET_TOKEN', token); // 更新 Vuex 中的 Token
  }
});
</script>

<style>
/* 全局样式 */
</style>

修改 permission.js(路由守卫)

优化路由守卫逻辑,确保 Token 和动态路由的正确处理。

import router from "@/router";
import store from "@/store";

router.beforeEach(async (to, from, next) => {
    console.log('Navigating from', from.path, 'to', to.path);

    // 白名单路径(无需登录即可访问)
    const whiteList = ['/adminlogin', '/login', '/register', '/forgetpassword'];

    // 从 localStorage 中获取 Token
    const token = localStorage.getItem('token') || store.getters.GET_TOKEN;
    const hasRoutes = store.state.hasRoutes;
    const menuList = store.getters.GET_MENULIST;

    // 设置页面标题
    document.title = store.getters.GET_WEBNAME;

    if (token) {
        // 有 Token 的情况
        if (!hasRoutes) {
            // 如果动态路由未加载,加载动态路由
            try {
                await bindRoute(menuList); // 动态加载路由
                store.commit("SET_ROUTES_STATE", true); // 标记路由已加载
                next({ ...to, replace: true }); // 重新跳转到目标路由
            } catch (error) {
                console.error('动态路由加载失败:', error);
                next('/login'); // 如果加载失败,跳转到登录页
            }
        } else {
            // 动态路由已加载,直接放行
            next();
        }
    } else {
        // 无 Token 的情况
        if (whiteList.includes(to.path)) {
            // 如果目标路径在白名单中,直接放行
            next();
        } else {
            // 否则跳转到登录页
            next("/login");
        }
    }
});

// 动态绑定路由
const bindRoute = (menuList) => {
    return new Promise((resolve) => {
        let newRoutes = router.options.routes;
        menuList.forEach(menu => {
            if (menu.children) {
                menu.children.forEach(m => {
                    // 将菜单转换为路由
                    let route = menuToRoute(m, menu.name);
                    if (route) {
                        newRoutes[0].children.push(route); // 添加到路由管理
                    }
                });
            }
        });
        // 重新添加到路由
        newRoutes.forEach(route => {
            router.addRoute(route);
        });
        resolve();
    });
};

// 菜单转成路由
const menuToRoute = (menu, parentName) => {
    if (!menu.component) {
        return null;
    } else {
        let route = {
            name: menu.name,
            path: menu.path,
            meta: {
                parentName: parentName
            }
        };
        route.component = () => import(`@/views${menu.component}.vue`); // 动态加载组件
        return route;
    }
};

修改 store 中的 Token 逻辑

确保在 store 中正确设置和清除 Token。

  1. 持久化存储:将 tokenmenuListwebName 和 userInfo 存储在 localStorage 中,而不是 sessionStorage,因为 sessionStorage 在页面刷新后会丢失。

  2. 初始化状态:在 store 初始化时,从 localStorage 中恢复状态。

  3. 退出登录逻辑:在 logout 操作中,清除 localStorage 和 sessionStorage,并重置 state

import router from '@/router';
import { createStore } from 'vuex';

export default createStore({
  state: {
    // 存储 token
    token: localStorage.getItem('token') || "", // 从 localStorage 中恢复 token
    userName: localStorage.getItem('userName') || "", // 从 localStorage 中恢复 userName
    hasRoutes: false,
    editableTabsValue: '/index',
    webName: localStorage.getItem('webName') || "", // 从 localStorage 中恢复 webName
    editableTabs: [
      {
        title: '首页',
        name: '/index'
      }
    ],
    menuList: JSON.parse(localStorage.getItem('menuList')) || [] // 从 localStorage 中恢复 menuList
  },
  getters: {
    GET_TOKEN(state) {
      return state.token;
    },
    GET_MENULIST(state) {
      return state.menuList;
    },
    GET_WEBNAME(state) {
      return state.webName;
    },
    GET_USERINFO(state) {
      return {
        username: state.userName
      };
    }
  },
  mutations: {
    SET_TOKEN(state, token) {
      state.token = token;
      localStorage.setItem('token', token); // 存储到 localStorage
    },
    SET_MENULIST(state, menuList) {
      state.menuList = menuList;
      localStorage.setItem('menuList', JSON.stringify(menuList)); // 存储到 localStorage
    },
    SET_WEBNAME(state, webName) {
      state.webName = webName;
      localStorage.setItem('webName', webName); // 存储到 localStorage
    },
    SET_USERINFO(state, userInfo) {
      state.userName = userInfo.username;
      localStorage.setItem('userName', userInfo.username); // 存储到 localStorage
    },
    SET_ROUTES_STATE(state, hasRoutes) {
      state.hasRoutes = hasRoutes;
    },
    ADD_TABS(state, tab) {
      if (state.editableTabs.findIndex(e => e.name === tab.path) === -1) {
        if (tab.path !== '/login' && tab.path !== '/register' && tab.path !== '/forgetpassword') {
          state.editableTabs.push({
            title: tab.name,
            name: tab.path
          });
        }
      }
      state.editableTabsValue = tab.path;
    },
    RESET_TABS(state) {
      state.editableTabsValue = '/index';
      state.editableTabs = [
        {
          title: '首页',
          name: '/index'
        }
      ];
    },
    // 定义重置 state 的 mutation 函数
    RESET_STATE(state) {
      // 重置各个属性值
      state.token = "";
      state.userName = "";
      state.hasRoutes = false;
      state.editableTabsValue = '/index';
      state.editableTabs = [
        {
          title: '首页',
          name: '/index'
        }
      ];
      state.menuList = [];
      // 清除 localStorage
      localStorage.removeItem('token');
      localStorage.removeItem('userName');
      localStorage.removeItem('webName');
      localStorage.removeItem('menuList');
    }
  },
  actions: {
    // 安全退出
    logout({ commit }) {
      // 调用 mutation 函数来重置 state
      commit('RESET_STATE');
      // 清除 sessionStorage
      sessionStorage.clear();
      // 跳转到登录页
      router.replace("/login");
      // 强制刷新页面以确保所有状态被清除
      window.location.reload();
    }
  },
  modules: {
  }
});
  1. 持久化存储

    • 将 tokenuserNamewebName 和 menuList 存储在 localStorage 中,确保刷新页面后数据不会丢失。

    • 在 state 初始化时,从 localStorage 中恢复数据。

  2. 退出登录逻辑

    • 在 logout 操作中,调用 RESET_STATE 重置 state,并清除 localStorage 和 sessionStorage

    • 使用 router.replace("/login") 跳转到登录页,并强制刷新页面以确保所有状态被清除。

  3. 状态恢复

    • 在 state 初始化时,从 localStorage 中恢复 tokenuserNamewebName 和 menuList,确保刷新页面后状态不会丢失。


http://www.kler.cn/a/592032.html

相关文章:

  • 大模型面试高频考点-显存占用
  • 大数据学习(72)-zookeeper选举机制
  • Linux top 命令详解:从入门到高级用法
  • Leetcode Hot 100 46.全排列
  • 在LORA训练中,LORA模型的矩阵的行列是多少
  • 《Python深度学习》第五讲:文本处理中的深度学习
  • [HelloCTF]PHPinclude-labs超详细WP-Level 1-FILE协议
  • fastapi+tcp+android在线聊天
  • Vue3是如何利用Proxy进行双向数据绑定的(二)?
  • 【开源免费】基于SpringBoot+Vue.JS智慧生活商城系统(JAVA毕业设计)
  • 从Excel文件中读取数据
  • Spring Boot 应用的接口访问从 HTTP 改为 HTTPS
  • 16. C语言二级指针
  • Kali Linux汉化教程:轻松设置中文界面
  • 【MySQL】关闭外键约束检查
  • 操作系统知识点32
  • Linux 部署 Spring Boot 项目, Web项目(2025版)
  • 分页优化之——游标分页
  • 微服务》》四个问题
  • 非洲能源商会:架起中非能源合作的桥梁