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

RBAC 权限控制 - 前端

获取基本信息

①登录后,②获取用户信息(角色、部门)、token,③获取菜单信息。

①登录 post http://codercba.com:5000/login获取登录相应信息和 token:

{
    "id": 1,
    "name": "coderwhy",
    "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwibmFtZSI6ImNvZGVyd2h5Iiwicm9sZSI6eyJpZCI6MSwibmFtZSI6Iui2hee6p-euoeeQhuWRmCJ9LCJpYXQiOjE3Mzxxxxxxxxx"
}

②get http://codercba.com:5000/users/1获取用户信息(角色、部门等)(携带 token):

{
    "id": 1,
    "name": "coderwhy",
    "realname": "coderwhy",
    "cellphone": 18812345678,
    "enable": 1,
    "createAt": "2021-01-02T10:20:26.000Z",
    "updateAt": "2021-01-03T04:50:13.000Z",
    "role": {
        "id": 1,
        "name": "超级管理员",
        "intro": "所有权限",
        "createAt": "2021-01-02T10:01:52.000Z",
        "updateAt": "2021-01-02T10:01:52.000Z"
    },
    "department": {
        "id": 1,
        "name": "总裁办",
        "parentId": null,
        "createAt": "2021-01-02T10:03:09.000Z",
        "updateAt": "2021-01-05T08:25:46.000Z",
        "leader": "coderwhy"
    }
}

③菜单信息:根据角色获取对应的菜单树。发送 GET 请求(携带 token):http://codercba.com:5000/role/1/menu

[
    {
        "id": 38,
        "name": "系统总览",
        "type": 1,
        "url": "/main/analysis",
        "icon": "el-icon-monitor",
        "sort": 1,
        "children": [
            {
                "id": 39,
                "url": "/main/analysis/overview",
                "name": "核心技术",
                "sort": 106,
                "type": 2,
                "children": null,
                "parentId": 38
            },
            {
                "id": 40,
                "url": "/main/analysis/dashboard",
                "name": "商品统计",
                "sort": 107,
                "type": 2,
                "children": null,
                "parentId": 38
            }
        ]
    },
    {
        "id": 1,
        "name": "系统管理",
        "type": 1,
        "url": "/main/system",
        "icon": "el-icon-setting",
        "sort": 2,
        "children": [
            {
                "id": 2,
                "url": "/main/system/user",
                "name": "用户管理",
                "sort": 100,
                "type": 2,
                "children": [
                    {
                        "id": 5,
                        "url": null,
                        "name": "创建用户",
                        "sort": null,
                        "type": 3,
                        "parentId": 2,
                        "permission": "system:users:create"
                    },
                    {
                        "id": 6,
                        "url": null,
                        "name": "删除用户",
                        "sort": null,
                        "type": 3,
                        "parentId": 2,
                        "permission": "system:users:delete"
                    },
                    {
                        "id": 7,
                        "url": null,
                        "name": "修改用户",
                        "sort": null,
                        "type": 3,
                        "parentId": 2,
                        "permission": "system:users:update"
                    },
                    {
                        "id": 8,
                        "url": null,
                        "name": "查询用户",
                        "sort": null,
                        "type": 3,
                        "parentId": 2,
                        "permission": "system:users:query"
                    }
                ],
                "parentId": 1
            },
            {
                "id": 3,
                "url": "/main/system/department",
                "name": "部门管理",
                "sort": 101,
                "type": 2,
                "children": [
                    {
                        "id": 17,
                        "url": null,
                        "name": "创建部门",
                        "sort": null,
                        "type": 3,
                        "parentId": 3,
                        "permission": "system:department:create"
                    },
                    {
                        "id": 18,
                        "url": null,
                        "name": "删除部门",
                        "sort": null,
                        "type": 3,
                        "parentId": 3,
                        "permission": "system:department:delete"
                    },
                    {
                        "id": 19,
                        "url": null,
                        "name": "修改部门",
                        "sort": null,
                        "type": 3,
                        "parentId": 3,
                        "permission": "system:department:update"
                    },
                    {
                        "id": 20,
                        "url": null,
                        "name": "查询部门",
                        "sort": null,
                        "type": 3,
                        "parentId": 3,
                        "permission": "system:department:query"
                    }
                ],
                "parentId": 1
            },
            {
                "id": 4,
                "url": "/main/system/menu",
                "name": "菜单管理",
                "sort": 103,
                "type": 2,
                "children": [
                    {
                        "id": 21,
                        "url": null,
                        "name": "创建菜单",
                        "sort": null,
                        "type": 3,
                        "parentId": 4,
                        "permission": "system:menu:create"
                    },
                    {
                        "id": 22,
                        "url": null,
                        "name": "删除菜单",
                        "sort": null,
                        "type": 3,
                        "parentId": 4,
                        "permission": "system:menu:delete"
                    },
                    {
                        "id": 23,
                        "url": null,
                        "name": "修改菜单",
                        "sort": null,
                        "type": 3,
                        "parentId": 4,
                        "permission": "system:menu:update"
                    },
                    {
                        "id": 24,
                        "url": null,
                        "name": "查询菜单",
                        "sort": null,
                        "type": 3,
                        "parentId": 4,
                        "permission": "system:menu:query"
                    }
                ],
                "parentId": 1
            },
            {
                "id": 25,
                "url": "/main/system/role",
                "name": "角色管理",
                "sort": 102,
                "type": 2,
                "children": [
                    {
                        "id": 26,
                        "url": null,
                        "name": "创建角色",
                        "sort": null,
                        "type": 3,
                        "parentId": 25,
                        "permission": "system:role:create"
                    },
                    {
                        "id": 27,
                        "url": null,
                        "name": "删除角色",
                        "sort": null,
                        "type": 3,
                        "parentId": 25,
                        "permission": "system:role:delete"
                    },
                    {
                        "id": 28,
                        "url": null,
                        "name": "修改角色",
                        "sort": null,
                        "type": 3,
                        "parentId": 25,
                        "permission": "system:role:update"
                    },
                    {
                        "id": 29,
                        "url": null,
                        "name": "查询角色",
                        "sort": null,
                        "type": 3,
                        "parentId": 25,
                        "permission": "system:role:query"
                    }
                ],
                "parentId": 1
            }
        ]
    },
    {
        "id": 9,
        "name": "商品中心",
        "type": 1,
        "url": "/main/product",
        "icon": "el-icon-goods",
        "sort": 3,
        "children": [
            {
                "id": 15,
                "url": "/main/product/category",
                "name": "商品类别",
                "sort": 104,
                "type": 2,
                "children": [
                    {
                        "id": 30,
                        "url": null,
                        "name": "创建类别",
                        "sort": null,
                        "type": 3,
                        "parentId": 15,
                        "permission": "system:category:create"
                    },
                    {
                        "id": 31,
                        "url": null,
                        "name": "删除类别",
                        "sort": null,
                        "type": 3,
                        "parentId": 15,
                        "permission": "system:category:delete"
                    },
                    {
                        "id": 32,
                        "url": null,
                        "name": "修改类别",
                        "sort": null,
                        "type": 3,
                        "parentId": 15,
                        "permission": "system:category:update"
                    },
                    {
                        "id": 33,
                        "url": null,
                        "name": "查询类别",
                        "sort": null,
                        "type": 3,
                        "parentId": 15,
                        "permission": "system:category:query"
                    }
                ],
                "parentId": 9
            },
            {
                "id": 16,
                "url": "/main/product/goods",
                "name": "商品信息",
                "sort": 105,
                "type": 2,
                "children": [
                    {
                        "id": 34,
                        "url": null,
                        "name": "创建商品",
                        "sort": null,
                        "type": 3,
                        "parentId": 16,
                        "permission": "system:goods:create"
                    },
                    {
                        "id": 35,
                        "url": null,
                        "name": "删除商品",
                        "sort": null,
                        "type": 3,
                        "parentId": 16,
                        "permission": "system:goods:delete"
                    },
                    {
                        "id": 36,
                        "url": null,
                        "name": "修改商品",
                        "sort": null,
                        "type": 3,
                        "parentId": 16,
                        "permission": "system:goods:update"
                    },
                    {
                        "id": 37,
                        "url": null,
                        "name": "查询商品",
                        "sort": null,
                        "type": 3,
                        "parentId": 16,
                        "permission": "system:goods:query"
                    }
                ],
                "parentId": 9
            }
        ]
    },
    {
        "id": 41,
        "name": "随便聊聊",
        "type": 1,
        "url": "/main/story",
        "icon": "el-icon-chat-line-round",
        "sort": 4,
        "children": [
            {
                "id": 42,
                "url": "/main/story/chat",
                "name": "你的故事",
                "sort": 108,
                "type": 2,
                "children": null,
                "parentId": 41
            },
            {
                "id": 43,
                "url": "/main/story/list",
                "name": "故事列表",
                "sort": 109,
                "type": 2,
                "children": [],
                "parentId": 41
            }
        ]
    }
]

用户登录提交 action:

async userLoginAction(loginData: ILoginData) {
  // 获取用户登录返回信息
  const res = await userLogin(loginData);
  const id = res.data.id;
  // 处理 token
  this.token = res.data.token;
  localCache.setCache(LOGIN_TOKEN, res.data.token);
  // 获取用户信息
  const userInfoResult = await getUserInfoById(id);
  this.userInfo = userInfoResult.data;
  localCache.setCache(USER_INFO, userInfoResult.data);
  // 获取权限
  const userMenusResult = await getUserMenusByRoleId(id);
  this.userMenus = userMenusResult.data;
  localCache.setCache(USER_MENUS, userMenusResult.data);
  // 动态添加路由
  const routes = mapMenus(this.userMenus);
  routes.forEach((route) => router.addRoute("main", route));
  // 路由跳转到首页
  router.push("/");
},

根据菜单动态注册路由

动态路由:根据用户的权限信息,动态的添加路由(而不是一次性注册所有路由)

  1. 基于角色(Role)的动态路由管理
const roles = {
    "superadmin":[所有的路由]=>router.main.children
    "admin":[一部分路由l,=>router.main.children
    "service":[少部分路由]=>router.main.children,
    "manager":[后面新添加的role]=>重新发布/后端返回这个对象(Gson数据,后端必须组织好这个json)
}

弊端:每增加一个角色,都要增加key/value

  1. 基于菜单(menu)的动态路由管理

userMenus:=>动态展示菜单(系统总览/核心技术/用户管理/角色管理/)

然后将菜单映射成路由对象。

原本的路由:

import { createRouter, createWebHistory } from "vue-router";
import { localCache } from "@/utils/cache";
import { LOGIN_TOKEN } from "@/constants";
import {firstMenu} from "@/utils/mapMenus";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      redirect: "/main"
    },
    {
      path: "/main",
      name: "main",
      component: () => import("@/views/main/MainView.vue")
    },
    {
      path: "/login",
      name: "login",
      component: () => import("@/views/login/LoginView.vue")
    },
    {
      path: "/:pathMatch(.*)",
      name: "NotFound",
      component: () => import("@/views/not-found/NotFound.vue")
    }
  ]
});

router.beforeEach((to, from, next) => {
  if (to.name !== "login" && !localCache.getCache(LOGIN_TOKEN)) next({ name: "login" });
  if (to.path === '/main') next({ path: firstMenu?.url });
  next();
});

export default router;

然后将动态路由单独划分一个路由文件夹:

在这里插入图片描述

dashboard.ts

export default {
  path: '/main/analysis/dashboard',
  name: 'dashboard',
  component: () => import('@/views/main/analysis/dashboard/dashboard.vue'),
  children: []
}

刚好和页面目录结构保持一致:

在这里插入图片描述

然后就可以在登录后触发 action 执行添加路由的操作(见上userLoginAction)

// 动态添加路由
const routes = mapMenus(this.userMenus);
routes.forEach((route) => router.addRoute("main", route));

映射菜单到路由的工具:

import type { RouteRecordRaw } from "vue-router";

export const loadLocalRoutes = () => {
  // 动态添加路由
  const localRoutes: RouteRecordRaw[] = [];
  // 获取本地路由文件
  const files: Record<string, any> = import.meta.glob("@/router/main/**/*.ts", {
    eager: true /*立刻获取文件信息,而不是懒加载获取*/
  });
  for (const path in files) {
    const module = files[path];
    localRoutes.push(module.default);
  }
  return localRoutes;
};

export let firstMenu: any = null;

/**
 * 将用户菜单转换为路由
 */
export const mapMenus = (userMenus: any[]) => {
  const localRoutes = loadLocalRoutes();
  const routes: RouteRecordRaw[] = [];
  // 根据菜单信息匹配该用户权限下的路由
  for (const menuItem of userMenus) {
    if (menuItem.children) {
      for (const submenu of menuItem.children) {
        const route = localRoutes.find((route) => route.path === submenu.url);
        if (route) {
          // 给父级菜单添加重定向
          if (!routes.find((r) => r.path === menuItem.url)) {
            routes.push({ path: menuItem.url, redirect: route.path });
          }
          // 添加二级菜单
          routes.push(route);
        }
        if (!firstMenu && route) {
          firstMenu = submenu;
          // console.log("=>(mapMenus.ts:35) firstMenu", firstMenu);
        }
      }
    }
  }
  return routes;
};

但是像上面这样做有个问题,刷新页面路由会丢失,因此我们写一个方法使动态路由持久化。

loadAsyncRoutes() {
  // 用户刷新 动态获取路由 防止因为刷新导致缓存的动态路由丢失
  if (this.token && this.userMenus && this.userInfo) {
    const routes = mapMenus(this.userMenus);
    routes.forEach((route) => {
      router.addRoute("main", route);
    });
  }
}

然后在入口文件main.ts中使用。

const registerStore = (app: App<Element>) => {
  app.use(pinia);
  const loginStore = useLoginStore();
  // todo 重新登录 如果 main 路由中已经含有子路由,则不执行动态添加路由
  loginStore.loadAsyncRoutes();
};

export default registerStore;
const app = createApp(App);

app.use(registerStore);

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

相关文章:

  • 【深度之眼cs231n第七期】笔记(三十一)
  • 某公交管理系统简易逻辑漏洞+SQL注入挖掘
  • 家政预约小程序11分类展示
  • 67-《蓝金花》
  • Kafka 入门与应用实战:吞吐量优化与与 RabbitMQ、RocketMQ 的对比
  • 【creo】CREO配置快捷键方式和默认单位
  • GESP2024年3月认证C++六级( 第三部分编程题(2)好斗的牛)
  • python基础语法(3) -------- 学习笔记分享
  • 99.17 金融难点通俗解释:归母净利润
  • Day42:列表的组合
  • 图像加解密
  • Linux内核组成
  • 品牌RWA化构建指南:资产数字化与价值共创
  • 【云原生】【适用小白】SpringCloud Alibaba开源Nacos切换到MSE Nacos
  • Helm Chart 实现 Kubernetes 应用多环境部署实战
  • 【黑龙江乡镇界】面图层arcgis数据shp格式乡镇名称和编码wgs84无偏移内容测评
  • SpringCloud系列教程:微服务的未来(十七)监听Nacos配置变更、更新路由、实现动态路由
  • 十年筑梦,再创鲸彩!庆祝和鲸科技十周年
  • 论文阅读(二):理解概率图模型的两个要点:关于推理和学习的知识
  • 协助工具-任意门导航
  • 996引擎 - 前期准备-配置开发环境
  • 2025寒假训练——天梯赛训练(1)
  • 【代码随想录】第二、三章-链表、哈希表
  • PyQt5之QtDesigner的若干配置和使用
  • pyautogui操控Acrobat DC pro万能PDF转Word,不丢任何PDF格式样式
  • 【识别代码截图OCR工具】