路由权限的分类与踩坑记录
路由的权限控制基本上分为两种,一种是完全由后端进行控制与分配权限,第二种是前端进行权限设置然后发送给后端,然后每次在登陆的时候调用这个路由权限的数组接口,根据这个接口的路由与基础路由进行合并。
1.后端进行控制与分配
在前端设置路由的时候,根据路由中是否有logIn_marking来判断是否需要需要进行权限判定,
css
{
path: 'router-face-swap-template',
name: 'faceSwapV2',
meta: {
title: '',
login_marking: 'faceSwapV2',
showName: '图片换脸V2'
},
component: (resolve) => import('./views/ai-face/face-swap-template/index.vue'),
}
如果有这个参数,则需要判定该用户是否有这个路由的权限,如果没有这个参数,则代表是常态路由,不需要进行判定,实现的时候用的是路由守卫的方式,将login_marking传到登录的仓库当中,进行islogin的判定
javascript
router.beforeEach((to, from, next) => {
// 拦截没有权限是路由
ViewUI.LoadingBar.start();
Util.title(to.meta.title);
if (to.meta.login_marking) {
if (!useUserStore(pinia).isLogin(to.meta.login_marking)) {
console.log('need login')
next({
path: `router-login?redirect=${to.name}`,
replace: true
})
return
}
console.log('LOGINED')
}
next();
});
router.afterEach((to, from, next) => {
ViewUI.LoadingBar.finish();
window.scrollTo(0, 0);
});
store中存储了登录信息和计算属性getters,将点击某个路由的login_marking传入到计算属性当中,进行判定,由于是数组数字,所以要加上映射,如果点击的路由在用户数组内,则代表该用户有这个权限。如果用户中没有则重定向到登录页面
javascript
state: () => {
return {
menus: [],
roles: [],
id: null,
name: '',
currentMenu: null,
}
},
getters: {
isLogin(state) {
return (mark) => state.roles.includes(menuMap[mark])
}
},
arduino
// 菜单权限map
export const menuMap = {
aiFaceFusion: '9',
aiFaceReplace: '8',
aiFaceEmotion: '7',
aigcConfig: '10',
aigcMultiTest: '11',
firstDayMail: '12',
faceSwapV2: '13',
aiLivePortrait: '14',
aliemo: '15',
vidu: '16',
}
2.前端端进行控制与分配
前端进行异步权限的设置会生成一个数组,根据接口传递给后端存储,每次登录的时候采用store中得到路由信息,由于是树状结构,因此采用递归的方法进行数组过滤,过滤出需要的数组路由,
typescript
const filterAsyncRoute = (asyncRoute: any, routes: any) => {
return asyncRoute.filter((item: any) => {
if (routes.includes(item.name)) {
if (item.children && item.children.length) {
item.children = filterAsyncRoute(item.children, routes)
}
return true
}
})
}
然后将常态路由和遍历出的异步路由进行拼接,得到所有的路由,然后赋值给用户
typescript
// 获取用户信息
async userInfo() {
const result: userInfoResponseData = await reqUserInfo()
if (result.code === 200) {
this.username = result.data.name
this.avatar = result.data.avatar
const userAsyncRoute = filterAsyncRoute(
cloneDeep(asyncRoute),
result.data.routes,
)
const arr = [...userAsyncRoute, ...anyRoute]
this.menuRoutes = [...constantRoute, ...arr]
arr.forEach((item: any) => {
router.addRoute(item)
})
return 'ok'
} else {
return Promise.reject(new Error(result.message))
}
},
踩坑记录1:跳转页面后404
当改变地址栏或者刷新页面的时候,页面会跳转到404,这是由于路由没有找到强匹配的地址,就会跳转到404页面
javascript
{
path: '/404',
name: '404',
component: () => import('@/views/404'),
hidden: true
},
{
path: '*',
redirect: '/404',
hidden: true
}
不在初始化路由的时候初始化,而是在解析接收的路由数据时拼接路由
javascript
// 将生成数组树结构的菜单并拼接404路由
const routes = Object.values(menusMap).concat(notFoundRoutes)
踩坑记录2:数据无法持久化
刷新页面会发现页面变空白,这是由于刷新页面router实例会重新初始化到初始状态,因此需要使用sessionstorage进行数据持久化
arduino
export const useUserStore = defineStore('useUserStore', {
// 开启数据缓存
persist: {
storage: sessionStorage,
},