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的模型
- RBAC0:RBAC0是权限最基础也是核心的模型,它包括用户/角色/权限,其中用户和角色是多对多的关系,角色和权限也是多对多的关系
- RBAC1:引用角色继承关系即角色间有上下级关系
- 角色间的继承关系可分为一般继承关系和受限继承关系。
- 一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承,这种设计可以给角色分组和分层,一定程度简化了权限管理的工作。
技术选型
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,运行服务,
4,运行前端:
npm install
或
yarn
npm run serve
或
yarn serve
5,访问 http://localhost:8081/
部分效果图展示
代码地址
本内容需要登录后查看
结尾
到这里我们的RBAC就讲完了!!欢迎注册涛哥博客哟!
阅读剩余
版权声明:
作者:涛哥
链接:https://ltbk.net/back/spring_family/spring-boot/article/1509.html
文章版权归作者所有,未经允许请勿转载。
作者:涛哥
链接:https://ltbk.net/back/spring_family/spring-boot/article/1509.html
文章版权归作者所有,未经允许请勿转载。
THE END