SpringBoot+Jwt+Redis+自定指令实现RBAC管理系统

TG-ADMIN 权限管理系统

项目简介

该项目是一款基于 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/

部分效果图展示

1d3f61fb6fd54b6dbf5013e6fd0bc8f0.png

322aac1c6f6c4020ba059ab84f813ec2.png  43c3f8336dd848a8a0ed3d11dbf76930.png

 

 58d2109c9fcb4af188db3ba2c3dd8f7f.png

 ad124eb446a043dea1adde4508fcac0f.png

6c8f01c05d9a41f5927f7d29a9e30bf7.png

ba1ff87cff9347a0a30a3e97e1264db3.png

023d1acdfd4f4db1bd43f98ea2ac74f5.png  be1b9b7d326c45e482b27dd8b3a06df3.png

 2bf7d5706c88492b9550d24ca7bfef7d.png

 801963b6d2894ba9bf94a6f5267332be.png

 73d24f75e3884915bb8e4ebc765ab844.png

 023a19c90d5841fdbaa2e4a7345bebf6.png

9d5e1332c6e344ed9c587dcd764936ef.png46e09ff408094aaa9d1b4200403cf3e4.png 

a526304a909940a6afe538039ef42d6f.png88bed406eaba4c9aabb9013ac2106ef4.png 

1193b8c9dca34204b159ae2f475fb425.png

 关注作者

涛哥博客 (*∩_∩*)欢迎你喔!一个专注于爱生活,爱代码,分享技术,交流学习,分享优质资源的独立个人技术博客站点https://ltbk.net

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晚上睡不着!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值