Springboot管理系統數據權限過濾——ruoyi實現方案

本文主要簡述,Ruoyi框架使用的權限過濾實現方案,實現簡單易懂。主要知識點有:

  1. 注解定義;
  2. 面向切面編程,在執行有數據權限注解的方法之前獲取用戶組織權限,拼接到domain對象的params參數中;

1. 注解定義

ruoyi數據權限涉及部門表sys_dept和用戶表sys_users表,這里僅用來定義sql查詢中部門表和用戶表的別名。


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataScope
{/*** 部門表的別名*/public String deptAlias() default "";/*** 用戶表的別名*/public String userAlias() default "";/*** 權限字符(用于多個角色匹配符合要求的權限)默認根據權限注解@ss獲取,多個權限用逗號分隔開來*/public String permission() default "";
}

2. 注解標記方法攔截后的處理

  1. 查看DataScopeAspect.class源碼,添加類注解:@Aspect、@Component。
  2. 權限類型共有五種:超管全部權限(直接跳過權限過濾)、全部數據權限、自定數據權限、部門數據權限、部門及以下數據權限、僅本人數據權限。分別對五種權限進行處理。(DataScope中定義的表別名用途就在這里)

/*** 數據過濾處理** @author ruoyi*/
@Aspect
@Component
public class DataScopeAspect
{/*** 全部數據權限*/public static final String DATA_SCOPE_ALL = "1";/*** 自定數據權限*/public static final String DATA_SCOPE_CUSTOM = "2";/*** 部門數據權限*/public static final String DATA_SCOPE_DEPT = "3";/*** 部門及以下數據權限*/public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";/*** 僅本人數據權限*/public static final String DATA_SCOPE_SELF = "5";/*** 數據權限過濾關鍵字*/public static final String DATA_SCOPE = "dataScope";@Before("@annotation(controllerDataScope)")public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable{clearDataScope(point);handleDataScope(point, controllerDataScope);}protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope){// 獲取當前的用戶LoginUser loginUser = SecurityUtils.getLoginUser();if (StringUtils.isNotNull(loginUser)){SysUser currentUser = loginUser.getUser();// 如果是超級管理員,則不過濾數據if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()){String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext());dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),controllerDataScope.userAlias(), permission);}}}/*** 數據范圍過濾** @param joinPoint 切點* @param user 用戶* @param deptAlias 部門別名* @param userAlias 用戶別名* @param permission 權限字符*/public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission){StringBuilder sqlString = new StringBuilder();List<String> conditions = new ArrayList<String>();for (SysRole role : user.getRoles()){String dataScope = role.getDataScope();if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)){continue;}if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))){continue;}if (DATA_SCOPE_ALL.equals(dataScope)){sqlString = new StringBuilder();conditions.add(dataScope);break;}else if (DATA_SCOPE_CUSTOM.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,role.getRoleId()));}else if (DATA_SCOPE_DEPT.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));}else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)){sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",deptAlias, user.getDeptId(), user.getDeptId()));}else if (DATA_SCOPE_SELF.equals(dataScope)){if (StringUtils.isNotBlank(userAlias)){sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));}else{// 數據權限為僅本人且沒有userAlias別名不查詢任何數據sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}}conditions.add(dataScope);}// 多角色情況下,所有角色都不包含傳遞過來的權限字符,這個時候sqlString也會為空,所以要限制一下,不查詢任何數據if (StringUtils.isEmpty(conditions)){sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));}if (StringUtils.isNotBlank(sqlString.toString())){Object params = joinPoint.getArgs()[0];if (StringUtils.isNotNull(params) && params instanceof BaseEntity){BaseEntity baseEntity = (BaseEntity) params;baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");}}}/*** 拼接權限sql前先清空params.dataScope參數防止注入*/private void clearDataScope(final JoinPoint joinPoint){Object params = joinPoint.getArgs()[0];if (StringUtils.isNotNull(params) && params instanceof BaseEntity){BaseEntity baseEntity = (BaseEntity) params;baseEntity.getParams().put(DATA_SCOPE, "");}}
}

在dataScopeFilter 方法最后的if方法塊中,通過向baseEntity中設置dataScope參數,將權限過濾條件放到了查詢條件中。

3. 分配數據權限

建立角色后,給角色分配數據權限,再給用戶分配一個或多個角色,這樣用戶即可獲取有權限的組織id。這里的問題即是只有支持組織權限,而且組織權限不能應用在不同場景下。
數據權限分配截圖:
在這里插入圖片描述

4. 權限應用

  1. 在要添加權限的方法體上添加注解并指定部門或人員表別名;且第一個參數是BaseEntity.class類的子類,這樣才能設置dataScope查詢參數。
    @DataScope(deptAlias = "d")public List<SysDept> selectDeptList(SysDept dept){return deptMapper.selectDeptList(dept);}
  1. sql中添加 ${dataScope}。
<select id="selectAllocatedList" parameterType="SysUser" resultMap="SysUserResult">select distinct u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.phonenumber, u.status, u.create_timefrom sys_user uleft join sys_dept d on u.dept_id = d.dept_idleft join sys_user_role ur on u.user_id = ur.user_idleft join sys_role r on r.role_id = ur.role_idwhere u.del_flag = '0' and r.role_id = #{roleId}<if test="userName != null and userName != ''">AND u.user_name like concat('%', #{userName}, '%')</if><if test="phonenumber != null and phonenumber != ''">AND u.phonenumber like concat('%', #{phonenumber}, '%')</if><!-- 數據范圍過濾 -->${params.dataScope}</select>

總結:
1. 邏輯簡單,代碼實現簡潔易懂;
2. 如果還有其他表要控制權限,也可以參考該實現方法,定義注解和攔截實現權限過濾;
3. 當不同場景都要按組織授權,那么目前Ruoyi的實現不能支持;(通過第2個辦法:按場景定義不同權限注解,那還需要實現前端配置功能,和相關權限查詢功能,還是復雜)

對于企業管理類軟件,通常權限控制不止組織,很可能會按其他業務類型控制權限,那如何通過一次編碼而且實用于所有業務場景呢?這種系統有以下需求:
1. 通過配置,動態指定權限表(數據庫表)或自定義數據類型;可使程序員更關注業務邏輯,減少代碼量;
2. 而不侵入SQL代碼,僅在方法體上指定相關權限控制編碼,可指定表名或字段名;減少對業務侵入;
3. 有多種場景都是按組織分配權限,但不同場景的權限是不一樣的,在使用時不能受影響;
4. 應用多樣化,可通過方法注釋指定使用的權限類型,也可通過前端傳參指定權限類型,或者傳參指定所有權限。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/213949.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/213949.shtml
英文地址,請注明出處:http://en.pswp.cn/news/213949.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AI:100-基于卷積神經網絡的農作物生長狀態監測

?? 本文選自專欄:人工智能領域200例教程專欄 從基礎到實踐,深入學習。無論你是初學者還是經驗豐富的老手,對于本專欄案例和項目實踐都有參考學習意義。 ??? 每一個案例都附帶有在本地跑過的核心代碼,詳細講解供大家學習,希望可以幫到大家。歡迎訂閱支持,正在不斷更新…

基于CMT2300A定制的模組諧波測量及調試事例

1.1 芯片介紹 CMT2300A華普微推出的一款超低功耗 Sub-1GHz 射頻收發器&#xff0c;是一款SPI接口射頻前端芯片&#xff0c;調制方式支持OOK (G)FSK 、(G)MSK&#xff0c;速率最大可以做到300 kbps&#xff0c;休眠大概1uA&#xff0c;功率最大可以做到20dB&#xff0c;但各國的…

Android 刪除瀏覽器導航頁面修改默認主頁

Android 刪除瀏覽器導航頁面修改默認主頁 近來收到客戶需求反饋&#xff0c;需要刪除瀏覽器導航頁面并將百度設置為默認主頁&#xff0c;具體修改參照如下&#xff1a; 刪除瀏覽器導航頁面&#xff1a; /vendor/mediatek/proprietary/packages/apps/Browser/src/com/android…

軟文怎么寫才能讓消費者行動起來?媒介盒子分享

軟文的本質是營銷&#xff0c;做營銷文案不是玩文字藝術&#xff0c;它需要洞察用戶需求&#xff0c;懂產品&#xff0c;了解賣點&#xff0c;懂營銷&#xff0c;懂消費心理&#xff0c;最終讓消費者行動起來。有些文案可能在你看起來遣詞造句和配圖都很一般&#xff0c;但就是…

分布式uuid常用的算法

1、雪花算法介紹 面試官&#xff1a;集群高并發情況下如何實現分布式唯一全局id生成&#xff1f; - 墨天輪 2、百度的UidGenerator 介紹&#xff0c;適合容器化配置&#xff0c;同時兼容springboot&#xff0c;只需要mysql數據庫&#xff0c; https://github.com/baidu/uid-…

Python辦公之Excel篇

1.準備環境 Python版本&#xff1a;3.6.5 IDE集成開發環境&#xff1a;pycharm Python庫選擇&#xff1a;openpyxl openpyxl操作的excel文件以xlsx結尾。 基礎命令 查看 Python 版本 python --version查看 pip 版本 pip --version安裝openxlsx pip install openpyxl -i…

9.靜態路由

靜態路由 中小型網絡都會用到&#xff0c;防火墻核心交換機用的很多&#xff0c;一般是用在出口 路由表&#xff1a;路由器用來轉發數據包唯一的依據 NextHop下一跳 Static靜態路由需要手動設置 ip route-static 目標網段 掩碼 下一跳例如&#xff1a;ip route-static 192…

QT講程序打包成安裝包讓任何人可以使用

&#x1f482; 個人主頁:pp不會算法v &#x1f91f; 版權: 本文由【pp不會算法v】原創、在CSDN首發、需要轉載請聯系博主 &#x1f4ac; 如果文章對你有幫助、歡迎關注、點贊、收藏(一鍵三連)和訂閱專欄哦 文章目錄 1、release模式下編譯2、windeploy 打包發布3、使用inno setu…

node.js express cors解決跨域

目錄 什么是跨域 示例 postman請求 前端請求 cors中間件解決跨域 流程 配置cors參數 什么是跨域 跨域&#xff08;Cross-Origin&#xff09;是指在 Web 開發中&#xff0c;當一個網頁的源&#xff08;Origin&#xff09;與另一個網頁的源不同時&#xff0c;就發生了跨域…

day6 arm

main.c #include "uartt.h"//封裝延時函數void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){//串口初始化uart4_init();//燈初始化led_init();//char a;char *s;while(1){myputchar(\n);myputchar(\r);//從串口讀取一個字符// amyget…

手把手教你反編譯小程序

本次實驗環境 操作系統: win10 10.0.19042 node: v14.17.0 微信開發者工具: Stable 1.05.2110290 前期準備 在電腦端安裝模擬器工具&#xff0c;這里以夜神模擬器為例&#xff0c; 在模擬器中安裝微信&#xff1a;用于微信打開小程序時加載小程序包。在模擬器中文件管理器&…

論文筆記:A review on multi-label learning

一、介紹 傳統的監督學習是單標簽學習&#xff0c;但是現實中一個實例可能對應多個標簽。這篇文章介紹了多標簽分類的定義和評價指標、多標簽學習的算法還有其他相關的任務。 二、問題相關定義 2.1 多標簽學習任務 假設 X R d X R^d XRd&#xff0c;表示d維的輸入空間&am…

面試經典150題(10-13)

leetcode 150道題 計劃花兩個月時候刷完&#xff0c;今天&#xff08;第四天&#xff09;完成了4道(10-13)150&#xff1a; 10. &#xff08;45. 跳躍游戲 II&#xff09;題目描述&#xff1a; 給定一個長度為 n 的 0 索引整數數組 nums。初始位置為 nums[0]。 每個元素 nums[…

日本服務器:確保其穩定性的幾個要點

?  在租用日本服務器時&#xff0c;用戶們大多一定會關注它的穩定性&#xff0c;其實這些顧及都是正常的。畢竟&#xff0c;網站要想正常運行&#xff0c;保障服務器穩定是關鍵。本文將討論有關如何保障日本服務器穩定性的一些有用技巧&#xff0c;希望對您有所幫助。 1.注重…

Linux定時循環備份指定文件或文件夾,每月永久備份留1份

備份需求&#xff1a;每天完成一次指定文件的備份&#xff0c;壓縮后存放到指定目錄 問題&#xff1a;備份時間長了以后占用空間較大&#xff0c;浪費存儲&#xff0c;實際歷史備份意義不大&#xff0c;并不需要永久保存。但是如果直接刪除可能導致無法恢復歷史狀態的數據。 …

SpringBoot 啟動加載器解析

計時器介紹 啟動加載器實戰 實現方式1 實現CommandLineRunner接口重寫run方法通過Order進行排序 示例: Component Order(1) public class FirstCommandlineRunner implements CommandLineRunner {Overridepublic void run(String... args) throws Exception {System.out.pr…

一篇上手機器學習

一、上手機器學習的幾個階段 上手機器學習&#xff0c;第一步當然是看完我的這篇文章啦~&#xff0c;然后就按以下步驟來就可以了&#xff1a; 學習Python編程語言&#xff1a;Python是一種易于學習的高級編程語言&#xff0c;廣泛應用于機器學習領域。你可以通過學習Python的…

第三節、項目支付功能實戰-微信支付平臺接入流程,小程序賬號注冊、商戶注冊

簡介 本篇介紹小程序的注冊流程、商戶平臺的注冊流程、以及小程序和商戶平臺如何進行綁定。 微信小程序注冊 由于項目中使用了小程序進行支付&#xff0c;所以首先來注冊小程序。小程序注冊網站如下&#xff1a;小程序注冊地址 小程序賬號注冊 1、鏈接頁面點擊“前往注冊”…

carla安裝中的問題

1、carla carla安裝完后&#xff0c;需要使用python調用API去更換地圖&#xff0c;增加車輛等 使用Python調用API過程中可能會報錯&#xff1a; 報錯1&#xff1a;carla API&#xff08;Carla包&#xff09;版本不對 **解決方法&#xff1a;**需要將這個目錄下的三個文件拷…

數學建模算法

算法部分 1. 評價類模型2. TOPSIS3. 線性規劃4. 聚類分析5. 預測模型6. 拉伊達準則(對異常值進行剔除)7. 數據擬合8. 圖論代碼練習1. 模擬圓周率2. 斐波那契數列3. 四只鴨子落在一個圓中概率4. 方程2: y" uy y,初值y(0) 1,y(0) 0 算法講解 matlab代碼大全 1. 評價類模型…