TG-ADMIN 权限管理系统

本文最后更新于2024.03.01-15:21,某些文章具有时效性,若有错误或已失效,请在下方留言或联系涛哥

项目简介

该项目是一款基于 SpringBoot + Vue2 + Jwt + ElementUi的 RBAC模型管理系统。

主要以自定义拦截器和jwt结合进行权限验证

通过自定义指令实现按钮级别权限,使用经典的RBAC模型

什么是RBAC?

1、RBAC模型概述

RBAC模型(Role-Based Access Control:基于角色的访问控制)模型是20世纪90年代研究出来的一种新模型,但其实在20世纪70年代的多用户计算时期,这种思想就已经被提出来,直到20世纪90年代中后期,RBAC才在研究团体中得到一些重视,并先后提出了许多类型的RBAC模型。其中以美国George Mason大学信息安全技术实验室(LIST)提出的RBAC96模型最具有代表,并得到了普遍的公认。

RBAC认为权限授权的过程可以抽象地概括为:Who是否可以对What进行How的访问操作,并对这个逻辑表达式进行判断是否为True的求解过程,也即是将权限问题转换为What、How的问题,Who、What、How构成了访问权限三元组;

2、RBAC的组成

在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限。

RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离(区别于ACL模型),极大地方便了权限的管理:

  • User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
  • Role(角色):不同角色具有不同的权限
  • Permission(权限):访问权限
  • 用户-角色映射:用户和角色之间的映射关系
  • 角色-权限映射:角色和权限之间的映射

 3,RBAC的模型

  1. RBAC0:RBAC0是权限最基础也是核心的模型,它包括用户/角色/权限,其中用户和角色是多对多的关系,角色和权限也是多对多的关系
  2. RBAC1:引用角色继承关系即角色间有上下级关系
    1. 角色间的继承关系可分为一般继承关系和受限继承关系。
    2. 一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承,这种设计可以给角色分组和分层,一定程度简化了权限管理的工作。

技术选型

1、系统环境

  • Java EE 8
  • Servlet 3.0
  • Apache Maven 3
  • Mysql 5.5
  • nodejs 14.21.3

2、主框架

  • Spring Boot 2.4.2
  • Jwt
  • Hutool
  • commons-pool2
  • poi-ooxml
  • Redis
  • Swagger
  • fastjson
  • commons-pool2
  • Mybatis-Plus-Generator

3、持久层

  • Apache MyBatis -Plus 3.5.x
  • Alibaba Druid 1.2.x

4、视图层

  • Aplayer
  • 一言
  • mavon-editor
  • echarts
  • Element ui
  • Vue2

内置功能

  • 欢迎页:介绍系统技术选型和统计
  • 文章管理:发布和修改文章,以及文章分类。
    • 所有文章
    • 文章分类
    • 文章标签
  • 系统管理:整个系统的管理包括用户菜单
    • 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
    • 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
    • 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
    • 字典管理:对系统中经常使用的一些较为固定的数据进行维护。
    • 文件管理:对系统上传文件进行管理。
  • 系统接口:根据业务代码自动生成相关的api接口文档。
  • 连接池监视:监视当期系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。

动态路由配置

//该文件专门用来创建和管理整个应用的路由器
import Vue from "vue";
import VueRouter from "vue-router"
​
Vue.use(VueRouter)
//地址和组件的对应关系
const routes = [
    {
        path: '/login',
        name: 'login',
        meta: {
            title: '登录'
        },
        component: () => import('@/views/Login')
    },
    {
        path: '/register',
        name: 'register',
        meta: {
            title: '注册'
        },
        component: () => import('@/views/Register')
    },
    {
        path: '/404',
        name: 'NotFound',
        component: () => import('@/views/404')
    },
    {
        path: '/401',
        name: 'NotFound',
        component: () => import('@/views/401')
    }
]
const router = new VueRouter({
    routes,
    base: process.env.BASE_URL,
    mode: "history"
})
​
export const resetRoutes = () => {
    router.matcher = new VueRouter({
        routes,
        base: process.env.BASE_URL,
        mode: "history"
    })
}
​
​
export const setRoutes = () => {
    const storeMenus = localStorage.getItem("menus");
    if (storeMenus) {
        //一级路由
        const homeRoutes = {
            path: '/', name: 'Home', component: () => import('../views/Home.vue'), redirect: '/index',
            meta: {
                isAuto: false,//是否需要路由组件拦截
                title: '首页'
            },
            children: [{
                path: 'person',
                name: 'Person',
                component: () => import('@/views/Person'),
                meta: {title: '个人主页'},
            }, {
                path: 'upwd',
                name: 'Upwd',
                component: () => import('../views/Upwd.vue'),
                meta: {title: '修改密码'}
            }]
        }
        const menus = JSON.parse(storeMenus);
        let itemPMenu;
        let itemCMenu;
        console.log(menus)
        //二级路由
        menus.forEach(item => {
            if (item.type ==='L' && item.path) {
                let itemMenu = {
                    path: item.path,
                    name: item.name,
                    component: () => import('../views/' + item.component + '.vue'),
                    meta: {
                        title: item.name,
                        pid: item.pid,
                        visible: item.visible
                    },
                    children: []
                }
                homeRoutes.children.push(itemMenu);
            } else if (item.type ==='M' && item.children.length) {
                itemPMenu = {
                    path: item.path,
                    name: item.name,
                    component: {render(c) {return c('router-view')}},
                    meta: {
                        title: item.name,
                    },
                    children: []
                }
                //三级路由
                item.children.forEach(child => {
                    console.log(child.name,child.type === 'C' && child.pid === item.id && child.type === 'C' && child.visible && child.status)
                    if (child.type === 'C' && child.pid === item.id && child.type === 'C' && child.visible && child.status) {
                        itemCMenu = {
                            path: child.path.replace("/", ""),
                            name: child.name,
                            component: () => import('../views/' + child.component + '.vue'),
                            meta: {
                                title: child.name,
                                pid: child.pid
                            }
                        }
                        itemPMenu.children.push(itemCMenu);
                    }
                })
                homeRoutes.children.push(itemPMenu);
            }
        })
        console.log(homeRoutes)
        const currentRoutes = router.getRoutes().map(v => v.name);
        if (!currentRoutes.includes("Home")) {
            router.addRoute(homeRoutes)
        }
    }
}
setRoutes();
​
// 前置路由守卫 在每个路由之前
// to 到哪去
// from 由哪来
// next 是否放行
router.beforeEach((to, from, next) => {
    if (to.matched.length === 0) {
        const storeMenus = localStorage.getItem("menus");
        if (storeMenus) {
            // next('/404');
        } else {
            next('/login');
        }
    } else {
        next();
    }
​
});
// 后置路由守卫
router.afterEach((to, from) => {
    document.title = to.meta.title;
​
})
export default router

自定义指令

permission.js

/*
自定义指令全局注册  按钮鉴权
*/
import Vue from "vue";
​
// 全局注册指令
Vue.directive('permission', {
    inserted(el, binding) {
        // console.log(el, binding);
        let {value} = binding;
        //获取当前用户权限表
        let permissionsList = JSON.parse(localStorage.getItem("permissions"));
        // console.log(permissionsList)
        //当前权限
        let prem = value[0];
        // 按钮图标
        let icon = value[0].icon;
        // console.log(prem);
        //当前用户获取的权限表为所有权限*
        if (permissionsList && permissionsList.length) {
            if (permissionsList[0] === "*") {
                return;
            }
            // 检测当前权限
            let permission = permissionsList.filter(item => item.permission === prem);
            // console.log(permission[0])
            // 没有权限,删除当前按钮
            if (permission.length === 0) {
                el.remove();
            } else {
                // console.log(el.children[0])
                el.children[0].className = permission[0].icon;
                el.children[1].textContent = permission[0].name;
                // console.log(el.classList.value)
            }
        }
​
    }
});

使用,如下列所示:

v-permission="['user:list:del']"

 运行

1,导入创建数据库并导入sql文件

2,导入idea中,修改配置文件

3,运行服务,http://localhost:8082/

4,运行前端:

npm install
或
yarn

npm run serve
或
yarn serve

5,访问 http://localhost:8081/

部分效果图展示

 

代码地址

本内容需要登录后查看

​结尾

到这里我们的RBAC就讲完了!!欢迎注册涛哥博客哟!

阅读剩余
THE END