vben admin 2.路由权限、前置守卫循环重定向死循环问题

vue路由权限思路都是基于导航守卫,然后addRoute添加权限去做的。因此你需要先了解1.vue导航守卫、2.vue路由权限全局前置守卫使用方法、3.vue Router 方法 addRoute的基本使用。

vue导航守卫

vue导航守卫官方文档地址:https://router.vuejs.org/zh/installation.html

vue路由权限全局前置守卫使用方法

一般都是基于全局前置守卫去做的
你可以使用 router.beforeEach 注册一个全局前置守卫:

const router = createRouter({ ... })

router.beforeEach((to, from) => {
  // ...
  // 返回 false 以取消导航
  return false
})

当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。

每个守卫方法接收两个参数:

to: 即将要进入的目标 用一种标准化的方式
from: 当前导航正要离开的路由 用一种标准化的方式
可以返回的值如下:

false: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
一个路由地址: 通过一个路由地址跳转到一个不同的地址,就像你调用 router.push() 一样,你可以设置诸如 replace: true 或 name: ‘home’ 之类的配置。当前的导航被中断,然后进行一个新的导航,就和 from 一样。

router.beforeEach(async (to, from) => {
   if (
     // 检查用户是否已登录
     !isAuthenticated &&
     // ❗️ 避免无限重定向
     to.name !== 'Login'
   ) {
     // 将用户重定向到登录页面
     return { name: 'Login' }
   }
 })

如果遇到了意料之外的情况,可能会抛出一个 `Error`。这会取消导航并且调用 [`router.onError()`](../../api/#onerror) 注册过的回调。

如果什么都没有,`undefined` 或返回 `true`**则导航是有效的**,并调用下一个导航守卫

以上所有都同 **`async` 函数** 和 Promise 工作方式一样:

```js
router.beforeEach(async (to, from) => {
 // canUserAccess() 返回 `true``false`
 const canAccess = await canUserAccess(to)
 if (!canAccess) return '/login'
})

vue Router 方法 addRoute

addRoute添加一条新的路由记录作为现有路由的子路由。如果路由有一个 name,并且已经有一个与之名字相同的路由,它会先删除之前的路由。(添加路由并不会触发新的导航。也就是说,除非触发新的导航,否则不会显示所添加的路由。)

方法1:

addRoute(parentName: string | symbol, route: RouteRecordRaw): () => void

在这里插入图片描述
方法2:

addRoute(route: RouteRecordRaw): () => void

在这里插入图片描述

vben admin搜索addRoute定位路由文件

src\store\modules\user.ts
在这里插入图片描述
usePermissionStore是在src\store\modules\permission.ts定义的
usePermissionStore用来拿取用户全部的权限,然后添加对应的路由

export const usePermissionStore = defineStore({
  id: 'app-permission',
  state: (): PermissionState => ({
    permCodeList: [],
    // Whether the route has been dynamically added
    isDynamicAddedRoute: false,
    // To trigger a menu update
    lastBuildMenuTime: 0,
    // Backstage menu list
    backMenuList: [],
    // menu List
    frontMenuList: [],
  }),

src\main.ts定义了引用规则
在这里插入图片描述

setupRouterGuard

根据单一职责去拆分成不同的独立函数去处理

export function setupRouterGuard(router: Router) {
  createPageGuard(router);
  createPageLoadingGuard(router);
  createHttpGuard(router);
  createScrollGuard(router);
  createMessageGuard(router);
  createProgressGuard(router);
  createPermissionGuard(router);
  createParamMenuGuard(router); // must after createPermissionGuard (menu has been built.)
  createStateGuard(router);
}

createPermissionGuard

router.beforeEach前置守卫

在这里使用前置守卫

export function createPermissionGuard(router: Router) {
  const userStore = useUserStoreWithOut();
  const permissionStore = usePermissionStoreWithOut();
  // router.beforeEach前置守卫处理路由:vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航,async指定为异步守卫
  router.beforeEach(async (to, from, next) => {
    if (
      from.path === ROOT_PATH &&
      to.path === PageEnum.BASE_HOME &&
      userStore.getUserInfo.homePath &&
      userStore.getUserInfo.homePath !== PageEnum.BASE_HOME
    ) {
      next(userStore.getUserInfo.homePath);
      return;
    }

    const token = userStore.getToken;

    // Whitelist can be directly entered:whitePathList定义了没有token可访问的文件路径,为了防止全局守卫在没有token的情况一直跳转页面而造成死循环
    if (whitePathList.includes(to.path as PageEnum)) {
      if (to.path === LOGIN_PATH && token) {
        const isSessionTimeout = userStore.getSessionTimeout;
        try {
          await userStore.afterLoginAction();
          if (!isSessionTimeout) {
            next((to.query?.redirect as string) || '/');
            return;
          }
        } catch {}
      }
      next();
      return;
    }

    // token does not exist
    if (!token) {
      // You can access without permission. You need to set the routing meta.ignoreAuth to true
      if (to.meta.ignoreAuth) {
        next();
        return;
      }

      // redirect login page
      const redirectData: { path: string; replace: boolean; query?: Recordable<string> } = {
        path: LOGIN_PATH,
        replace: true,
      };
      if (to.path) {
        redirectData.query = {
          ...redirectData.query,
          redirect: to.path,
        };
      }
      next(redirectData);
      return;
    }

    // Jump to the 404 page after processing the login
    if (
      from.path === LOGIN_PATH &&
      to.name === PAGE_NOT_FOUND_ROUTE.name &&
      to.fullPath !== (userStore.getUserInfo.homePath || PageEnum.BASE_HOME)
    ) {
      next(userStore.getUserInfo.homePath || PageEnum.BASE_HOME);
      return;
    }

    // get userinfo while last fetch time is empty
    if (userStore.getLastUpdateTime === 0) {
      try {
        await userStore.getUserInfoAction();
      } catch (err) {
        next();
        return;
      }
    }

    if (permissionStore.getIsDynamicAddedRoute) {
      next();
      return;
    }

    const routes = await permissionStore.buildRoutesAction();

    routes.forEach((route) => {
      router.addRoute(route as unknown as RouteRecordRaw);
    });

    router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw);

    permissionStore.setDynamicAddedRoute(true);

    if (to.name === PAGE_NOT_FOUND_ROUTE.name) {
      // 动态添加路由后,此处应当重定向到fullPath,否则会加载404页面内容
      next({ path: to.fullPath, replace: true, query: to.query });
    } else {
      const redirectPath = (from.query.redirect || to.path) as string;
      const redirect = decodeURIComponent(redirectPath);
      const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
      next(nextData);
    }
  });
}

白名单的作用:避免前置守卫循环重定向导致死循环

路径:/login
vben admin为什么使用前置守卫?
无论走到哪一个页面,都需要先经过前置守卫,然后才能判断出来哪些可以访问,可以理解为页面的拦截器,前置守卫会先判断token(没有token,即没有登录,就会让你跳转到登录页面),然后再判断对应权限。

前置守卫循环重定向导致死循环?
前置守卫会先判断token(没有token,即没有登录,就会让你跳转到登录页面/login),然后再判断对应权限。跳转页面会触发前置守卫,那么没有token跳到/login登录页面的时候,也会触发,就会产生二次判断token的现象,这样一直往复,就会造成死循环,所以,就必须用一个白名单,来定义那些没有token并允许进行访问的页面。

const whitePathList: PageEnum[] = [LOGIN_PATH];
const LOGIN_PATH = PageEnum.BASE_LOGIN;
export enum PageEnum {
  // basic login path
  BASE_LOGIN = '/login',
  // basic home path
  BASE_HOME = '/dashboard',
  // error page path
  ERROR_PAGE = '/exception',
  // error log page path
  ERROR_LOG_PAGE = '/error-log/list',
}

router.addRoute添加路由权限

在这里插入图片描述

getMenuList()获取菜单

src\store\modules\permission.ts

在这里插入图片描述

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值