SpringBoot實現(xiàn)RBAC權限校驗模型的示例
最近想做一個管理系統(tǒng),記錄下自己的消費記錄,實現(xiàn)個文件上傳之類的功能,正好最近看了下RBAC模型,想學習一下,在這分享下實現(xiàn)過程,方便以后查看
RBAC模型簡單來說就是創(chuàng)建5張表(用戶表,角色表,權限表,角色與用戶表,角色與權限表)
通過給用戶分配不同角色,以及給角色分配不同權限,實現(xiàn)用戶的權限控制
不過這次我做的項目是個后臺管理系統(tǒng),主要是自己和朋友使用,所以角色其實就分成了管理員和普通用戶,因此我省略掉了角色和角色權限表,這樣雖然不夠靈活,但畢竟使用的人少,所以管理起來也非常方便
數(shù)據(jù)庫表
數(shù)據(jù)庫主要創(chuàng)建三張表
用戶表:
主要用來使用用戶登錄
- id 用戶主鍵
- account 登錄賬號
- password 登錄密碼
- type 區(qū)分普通角色和管理員身份
- username 用戶名
- status 用戶的狀態(tài)
+-----------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | account | char(15) | YES | | NULL | | | password | varchar(50) | YES | | NULL | | | type | int | YES | | NULL | | | username | varchar(20) | YES | | NULL | | | status | int | YES | | NULL | | +-----------+-------------+------+-----+---------+----------------+
權限表:
主要用來記錄權限信息
- id 權限主鍵
- name 權限名稱
- parent_id 父權限的id 如果沒有父權限則為null
- status 權限是否啟用
- remark 權限備注
- level 權限的層級,主要用來排序的字段
+-------------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+-------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | name | varchar(20) | YES | | NULL | | | parent_id | int | YES | | NULL | | | status | int | YES | | NULL | | | remark | varchar(50) | YES | | NULL | | | create_user | int | YES | | NULL | | | create_date | date | YES | | NULL | | | update_user | int | YES | | NULL | | | update_date | date | YES | | NULL | | | level | int | YES | | NULL | | +-------------+-------------+------+-----+---------+----------------+
用戶權限表:
實現(xiàn)多對多關系的表
- id 主鍵
- user_id 用戶的ID
- permission_id 權限的ID
+---------------+------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------------+------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | user_id | int | YES | MUL | NULL | | | permission_id | int | YES | | NULL | | +---------------+------+------+-----+---------+----------------+
主要解釋下權限表,因為管理系統(tǒng)需要根據(jù)權限判斷是否提供菜單,因此我將菜單作為了一級權限(parent_id=null),一級權限下的增刪改查等操作定義為二級權限(parent_id=對應的一級權限)
如果用戶擁有某些權限就在用戶權限表中創(chuàng)建一條記錄
后端實現(xiàn)
后端實現(xiàn)起來非常的方便,我使用的方法是定義一個注解通過AOP掃描這個注解,然后判斷當前用戶是否擁有對應的權限,如果沒有直接拋出異常,通過全局異常處理器進行捕捉
定義注解類:
因為后臺管理實現(xiàn)的非常簡單,幾乎不存在多個表之間相互嵌套,因此我用了個value表示當前Controller需要使用到的權限的ID
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface PermissionCheckAnnotation { int value(); }
定義枚舉類:
主要用來定義對應權限的ID值
public class PermissionEnum { public final static int USER_QUERY = 14; public final static int USER_UPDATE = 15; public final static int USER_CREATE = 16; }
定義AOP切面
@Pointcut("execution(掃描當前包中的全部文件) && @annotation(annotation.PermissionCheckAnnotation)") public void pt(){} @Around("pt()") public Object PermissionCheck(ProceedingJoinPoint jp) throws Throwable { MethodSignature signature = (MethodSignature) jp.getSignature(); // 反射拿到權限ID PermissionCheckAnnotation annotation = signature.getMethod().getAnnotation(PermissionCheckAnnotation.class); int value = annotation.value(); // 獲取用戶ID Integer userId = UserContext.getUserId(); // 查詢當前用戶和權限ID之間是否存在關聯(lián)關系,如果不存在拋出異常 Integer hasPermission = userPermissionMapper.hasPermission(value, userId); if (hasPermission == null || hasPermission == 0){ throw new PermissionException("權限不足,請聯(lián)系管理員"); } return jp.proceed(); }
定義controller
@PermissionCheckAnnotation(PermissionEnum.USER_CREATE) @PostMapping("/createUser") public Result<String> createUser() { }
后端修改權限
前端將修改完的權限發(fā)送過來,大體格式就是一個整數(shù)數(shù)組([1,2,3,4,5]),表示當前用戶的全部權限的ID集合
權限更新的主要思路就是將該用戶的全部權限刪除,再重建
如果嫌棄太慢的話,我覺得可以取交集之類的操作,提升效率,或者再額外定義一個字段,用來判斷當前用戶權限的狀態(tài)(delete字段)這樣應該會比delete insert效率高一些
不過俺是個菜鳥小白+懶狗,怎么簡單怎么來了
刪除用戶不擁有的權限
<delete id="deletePermission"> delete from user_permission where user_id = #{userId} and permission_id not in <foreach item="item" collection="permissionList" open="(" close=")" separator=","> #{item} </foreach> </delete>
插入新權限
因為是全部插入,權限可能會產(chǎn)生重復,因此我給user_id和permission_id添加唯一約束,并在插入時忽略錯誤
<insert id="createPermission"> insert ignore into user_permission <foreach collection="permissionList" open="values" item="item" separator=","> (null,#{userId},#{item}) </foreach> </insert>
前端
我前端不是很好,只會單純畫個界面,因此我還沒有實現(xiàn)按鈕級別的權限控制,不過大體思路是自定義指令,然后檢查是否擁有對應的權限,如果沒有權限直接隱藏該按鈕
至于菜單級別的權限,我的做法是每次都發(fā)送一個請求,判斷能用多少菜單,即使被人扒出來其他界面,后端也做了權限的校驗,數(shù)據(jù)還是拿不到
這里主要記錄下如何在前端修改權限
查詢返回的數(shù)據(jù)格式大致如下
{ id: 15 level: 2 name: "操作查詢" parentId: 1 remark: "操作菜單下的查詢功能" status: 1 } { id: 1 level: 1 name: "操作菜單" parentId: null remark: "操作菜單" status: 1 }
我想要在element-Plus中的樹形控件中實現(xiàn)展示,操作功能,大致需要將上面的數(shù)據(jù)變成這樣的格式
const data: Tree[] = [ { label: 'Level one 1', children: [ { label: 'Level two 1-1', }, ], }, ]
這里通過dfs實現(xiàn)的(同層權限之間不會產(chǎn)生交集,所以應該能用最小生成樹完成,不過俺是個懶狗,寫了個最簡單的方式)
const st = new Map(); for (let i = 0; i < res.length; i++) { st.set(res[i].id, 0); } const list = []; for (let i = 0; i < res.length; i++) { if (res[i].parentId == null) { const children = dfs(i, res); list.push(children); } } tree.value = list; function dfs(idx, res) { st.set(res[idx].id, 1); const children = [] for (let i = 0; i < res.length; i++) { if (st.get(res[i].id) == 1) { continue; } if (res[i].parentId == res[idx].id) { children.push(dfs(i, res)) } } if (children.length == 0) return { id: res[idx].id, label: res[idx].name } return { id: res[idx].id, label: res[idx].name, children } }
然后把操作完的數(shù)據(jù)扔到elTree中
<el-tree v-loading="treeLoading" @check="checkChange" style="max-width: 200px" ref="elTreeRef" :data="tree" :props="defaultProps" show-checkbox node-key="id" />
然后就是用戶的初始權限,通過點擊不同的用戶向后端查詢該用戶擁有的權限,el-tree有個屬性可以設置默認勾選的復選框,但我不知道為什么點擊不同用戶時,樹中上一個勾選的復選框不會被取消,因此使用了el-tree的方法,當點擊不同用戶時觸發(fā),傳入的數(shù)據(jù)是一個整數(shù)數(shù)組([1,2,3]),表示勾選的節(jié)點的id,需要在el-tree標簽中提前指定node-key
elTreeRef.value.setCheckedKeys(hasPermission.value)
最后就是權限修改,大致就是用戶點擊不同的復選框,選擇或取消不同的權限,通過綁定@check事件,然后獲取里面的數(shù)據(jù),這里說一下當點擊子復選框時,父復選框也會觸發(fā)一次,當父選框下的全部子復選框都取消時,父復選框也會取消,通過下面這個方法能拿到當前勾選的復選框,然后發(fā)送數(shù)據(jù)
const checkChange = function (data1, data2) { currentPermission = [...data2.checkedKeys, ...data2.halfCheckedKeys] }
總結
就這樣,實現(xiàn)了一個簡單的權限校驗模型,雖然缺少了角色表,但大體功能上大差不差,添加上角色表后,可能還得考慮權限的遷移,權限的分配等等問題
不過對于一個簡單的系統(tǒng)來說還是十分夠用力
還有為什么csdn還對封面圖大小設置了,把我的卡茲米都壓成什么樣了??????
到此這篇關于SpringBoot實現(xiàn)RBAC權限校驗模型的示例的文章就介紹到這了,更多相關SpringBoot RBAC權限校驗 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- SpringBoot快速設置攔截器并實現(xiàn)權限驗證的方法
- SpringBoot+Spring Security+JWT實現(xiàn)RESTful Api權限控制的方法
- SpringBoot集成Shiro進行權限控制和管理的示例
- SpringBoot2.0整合Shiro框架實現(xiàn)用戶權限管理的示例
- SpringBoot 監(jiān)控管理模塊actuator沒有權限的問題解決方法
- Springboot 如何使用 SaToken 進行登錄認證、權限管理及路由規(guī)則接口攔截
- 詳解關于springboot-actuator監(jiān)控的401無權限訪問
- SpringBoot + Shiro前后端分離權限
- 解決springboot+shiro 權限攔截失效的問題
- springboot集成springsecurity 使用OAUTH2做權限管理的教程
- Springboot+mybatis-plus+注解實現(xiàn)數(shù)據(jù)權限隔離
相關文章
Java中的CyclicBarrier循環(huán)柵欄解析
這篇文章主要介紹了Java中的CyclicBarrier循環(huán)柵欄解析,從字面上的意思可以知道,這個類的中文意思是"循環(huán)柵欄",大概的意思就是一個可循環(huán)利用的屏障,它的作用就是會讓所有線程都等待完成后才會繼續(xù)下一步行動,需要的朋友可以參考下2023-12-12解決程序啟動報錯org.springframework.context.ApplicationContextExcept
文章描述了一個Spring Boot項目在不同環(huán)境下啟動時出現(xiàn)差異的問題,通過分析報錯信息,發(fā)現(xiàn)是由于導入`spring-boot-starter-tomcat`依賴時定義的scope導致的配置問題,調整依賴導入配置后,解決了啟動錯誤2024-11-11springboot整合liteflow的實現(xiàn)示例
本文主要介紹了在Spring Boot項目中整合Liteflow規(guī)則引擎,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-12-12Idea2019創(chuàng)建Springboot Web項目的方法步驟
這篇文章主要介紹了Idea2019創(chuàng)建Springboot Web項目的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10springMVC框架下JQuery傳遞并解析Json數(shù)據(jù)
json作為一種輕量級的數(shù)據(jù)交換格式,在前后臺數(shù)據(jù)交換中占據(jù)著非常重要的地位,這篇文章主要介紹了springMVC框架下JQuery傳遞并解析Json數(shù)據(jù),有興趣的可以了解一下。2017-01-01Java根據(jù)前端傳回的圖片生成pdf并且加密碼和水印
這篇文章主要為大家詳細介紹了java如何根據(jù)前端傳回的png圖片數(shù)組,后端加水印加密碼生成pdf并返回給前端,感興趣的小伙伴可以參考一下2025-01-01java開發(fā)之SQL語句中DATE_FORMAT函數(shù)舉例詳解
要將日期值格式化為特定格式,請使用DATE_FORMAT函數(shù),下面這篇文章主要給大家介紹了關于java開發(fā)之SQL語句中DATE_FORMAT函數(shù)的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-05-05