vben admin 2.路由权限、前置守卫循环重定向死循环问题
vben admin 路由权限、前置守卫循环重定向死循环问题vue导航守卫vue路由权限全局前置守卫使用方法vue Router 方法 addRoutevben admin搜索addRoute定位路由文件setupRouterGuardcreatePermissionGuardrouter.beforeEach前置守卫白名单的作用:避免前置守卫循环重定向导致死循环router.addRoute添加
vben admin 路由权限、前置守卫循环重定向死循环问题
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
更多推荐
所有评论(0)