1.从服务端获取用户的 [基础信息] 和 [权限信息]并构建动态路由和菜单
问题1:为什么这么做:
- 一般开发需要权限的系统时,都会有 登录 步骤
- 单页应用每次刷新时,内存中的数据就会呗清空,如果每次刷新,执行一次登录步骤就是很不合理的
- 设计一次登录就是为了获取用户的授权 access token,并持久化到localstorage中,之后用户每次打开页面或者刷新页面时,都可以利用这个 token 去后端获取用户的真实信息
问题2:为什么不把用户心也存到localstorage来少一次请求?
- 用户信息存在loacalstorage后,使用者打开控制台,直接修改其中权限信息,如果用户角色是user,被改成了admin。刷新页面的时候就是admin的信息了。
问题3:为什么不每次都调用登录?
- 如果每次刷新都要登录认证,那么用户的账户及密码则不可保障安全
- 要登录必须要账户密码或者相同功能的认证信息代替
问题4:access token 不也是不能保障安全吗?
- 用户在此进行登录,代表认同该设备,保存用户的token可以进行快速身份认证
- 当用户认为 token 发生泄露或者不安全时,可以根据相关的服务端 token 设计规则,删除token
2.在Vue中应该怎么配置导航栏路由
? ? ? ? 一般情况下,我们的路由都是根据router/index.ts里面手动配置的,然后在根据加载路由的时候获取导航栏
export const routes: MenuDataItem[] = [
{
name: 'index',
path: '/',
redirect: '/workplace',
component: Layout,
children: [
{
path: '/workplace',
name: 'Workplace',
meta: { icon: 'HistoryOutlined', title: 'pages.dashboard.workplace.title', lock: true },
component: h(RouteView, null, () => h(AsyncWorkplace)),
},
]
}
]
如果需要动态配置时,首先需要在后端构建好数据,获取数据以后,封装成路由格式,或者在返回前端以后再进行封装
// 生成常用路由
// getCurrentUserNav() 是后端获取需要显示模块的数据,进行返回
// generator 数据重构生成一个完整的路由
export const generatorDynamicRouter = () => {
return new Promise<MenuDataItem>((resolve, reject) => {
getCurrentUserNav()
.then(menuNavRes => {
// root id = 0;
const routes = generator(menuNavRes.data, 0, undefined) as MenuDataItem[];
// routes.push(notFoundRouter);
rootRouter.children = routes;
resolve(rootRouter);
})
.catch(err => {
reject(err);
});
});
};
?
// 根级菜单
const rootRouter: MenuDataItem = {
name: 'index',
path: '/',
redirect: '/dashboard',
meta: {
title: '首页',
},
component: () => import('@/layouts/index.vue'),
children: [] as MenuDataItem[],
};
// 构建一个路由
export const generator = (
routeMap: RouteItem[],
parentId: string | number,
routeItem?: RouteRecordRaw | MenuDataItem,
) => {
return routeMap
.filter(item => item.parentId === parentId)
.map(item => {
const { title, hideInMenu, hideChildrenInMenu, target, icon, authority } = item.meta || {};
const currentRouter: MenuDataItem = {
// 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace
path: item.path || `${(routeItem && routeItem.path) || ''}/${item.name}`,
// 路由名称,建议唯一
name: item.name || `${item.id}`,
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
meta: {
title,
icon: icon || undefined,
hideInMenu,
hideChildrenInMenu,
target: target,
authority: authority,
},
// 该路由对应页面的 组件 (动态加载 @/views/ 下面的路径文件)
component:
item.component && defineRouteComponentKeys.includes(item.component)
? defineRouteComponents[item.component]
: () => import(/* @vite-ignore */ `../views/${item.component}.vue`),
};
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
if (!currentRouter.path.startsWith('http')) {
currentRouter.path = currentRouter.path.replace('//', '/');
}
// 重定向
item.redirect && (currentRouter.redirect = item.redirect);
// 子菜单,递归处理
currentRouter.children = generator(routeMap, item.id, currentRouter);
if (currentRouter.children === undefined || currentRouter.children.length <= 0) {
delete currentRouter.children;
}
return currentRouter;
})
.filter(item => item);
};
|