使用MyBatis-Plus實現數據權限功能

什么是數據權限

數據權限是指系統根據用戶的角色、職位或其他屬性,控制用戶能夠訪問的數據范圍。與傳統的功能權限(菜單、按鈕權限)不同,數據權限關注的是數據行級別的訪問控制。

常見的數據權限控制方式包括:

  • 部門數據權限:只能訪問本部門數據

  • 個人數據權限:只能訪問自己的數據

  • 自定義數據范圍:通過特定規則限制數據訪問

MyBatis-Plus實現數據權限的方案

實現完整例子

下面是一個完整的基于MyBatis-Plus和Spring Security的數據權限實現示例:

?1. 數據權限配置類

@Configuration
public class MybatisConfig {@Beanpublic ConfigurationCustomizer mybatisConfigurationCustomizer(){return configuration -> configuration.setObjectWrapperFactory(new MapWrapperFactory());}@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 1.添加數據權限插件interceptor.addInnerInterceptor(new DataPermissionInterceptor(new DataPressionConfig()));PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();// 分頁插件paginationInnerInterceptor.setOptimizeJoin(false);paginationInnerInterceptor.setOverflow(true);paginationInnerInterceptor.setDbType(DbType.POSTGRE_SQL);interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;}
}

    ?2. 數據權限攔截器
    @Slf4j
    @Component
    public class DataPressionConfig implements DataPermissionHandler {@Overridepublic Expression getSqlSegment(Expression where, String mappedStatementId) {try {if(null==mappedStatementId){return null;}Class<?> mapperClazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);// 獲取自身類中的所有方法,不包括繼承。與訪問權限無關Method[] methods = mapperClazz.getDeclaredMethods();for (Method method : methods) {DataScope dataScopeAnnotationMethod = method.getAnnotation(DataScope.class);if(null==dataScopeAnnotationMethod){continue;}//spring aoc里拿方法參數Parameter[] parameters= method.getParameters();if (parameters.length > 0) {log.info("方法參數:" +  dataScopeAnnotationMethod.oneselfScopeName() );}if (ObjectUtils.isEmpty(dataScopeAnnotationMethod) || !dataScopeAnnotationMethod.enabled()) {continue;}if (method.getName().equals(methodName) || (method.getName() + "_COUNT").equals(methodName) || (method.getName() + "_count").equals(methodName)) {return buildDataScopeByAnnotation(dataScopeAnnotationMethod,mappedStatementId);}}} catch (ClassNotFoundException e) {e.printStackTrace();}return null;}/*** DataScope注解方式,拼裝數據權限** @param dataScope* @return*/private Expression buildDataScopeByAnnotation(DataScope dataScope,String mapperId) {Map<String, Object> params = DataPermissionContext.getParams();if (params == null || params.isEmpty()) {return null;}Object areaCodes = params.get(mapperId);List<String> dataScopeDeptIds=  (List<String>) areaCodes;// 獲取注解信息String tableAlias = dataScope.tableAlias();String areaCodes= dataScope.areaCodes();Expression expression = buildDataScopeExpression(tableAlias,   areaCodes, dataScopeDeptIds);return expression == null ? null : new Parenthesis(expression);}/*** 拼裝數據權限** @param tableAlias        表別名* @param oneselfScopeName  本人限制范圍的字段名稱* @param dataScopeDeptIds  數據權限部門ID集合,去重* @return*/private Expression buildDataScopeExpression(String tableAlias,  String areaCodes, List<String> dataScopeDeptIds) {/*** 構造部門里行政區劃 area_code 的in表達式。*/try {String sql=tableAlias + "." + areaCodes+" in (";for(String areaCode:dataScopeDeptIds){sql+="'"+areaCode+"',";}sql=sql.substring(0,sql.length()-1)+")";Expression selectExpression = CCJSqlParserUtil.parseCondExpression(sql, true);return selectExpression;} catch (JSQLParserException e) {throw new RuntimeException(e);}}}

    3. 存儲spring aop切面拿到的數據

      ? ?

      public class DataPermissionContext {private static final ThreadLocal<Map<String, Object>> CONTEXT = new ThreadLocal<>();public static void setParams(Map<String, Object> params) {CONTEXT.set(params);}public static Map<String, Object> getParams() {return CONTEXT.get();}public static void clear() {CONTEXT.remove();}
      }

        ?4.?定義數據權限注解
        @Inherited
        @Target({ElementType.METHOD, ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        public @interface DataScope {/*** 是否生效,默認true-生效*/boolean enabled() default true;/*** 表別名*/String tableAlias() default "";/*** 本人限制范圍的字段名稱*/String areaCodes() default "area_code";}
        5. 實現AOP切面
        @Slf4j
        @Aspect
        @Component
        public class DataAspet {@Pointcut("@annotation(DataScope)")public void logPoinCut() {}@Before("logPoinCut()")public void saveSysLog(JoinPoint joinPoint) {//從切面織入點處通過反射機制獲取織入點處的方法MethodSignature signature = (MethodSignature) joinPoint.getSignature();//獲取切入點所在的方法Method method = signature.getMethod();DataScope scope = method.getAnnotation(DataScope.class);Map<String, Object> paramValues = new HashMap<>();Object[] args = joinPoint.getArgs();Parameter[] parameters = method.getParameters();if(null!=parameters&&parameters.length>0) {//添加到最后一個參數log.info("參數名稱:{}",signature.getName()+":"+signature.getDeclaringTypeName());paramValues.put(signature.getDeclaringTypeName()+"."+signature.getName(), args[parameters.length-1]);DataPermissionContext.setParams(paramValues);}else{DataPermissionContext.setParams(null);}}}

        6. mapper里注解使用數據權限

        tableAlias里你的sql里面要插入數據權限字段的表別名。

        比如:

        select count(*) as total,d.type?as type from bus_device d group by d.type

        spring aop?會讀取mapper方法最后一個參數,然后切入Sql變成

        select count(*) as total,d.type?as type from bus_device d where

        d.area_code in(#{areaCodes} )? group by d.type

            @DataScope(tableAlias = "d")List<DeviceTypeCountDto> listByApplicationCategory(@Param(  @Param("type") String type,List<String> areaCodes);
        

        注意事項

        1. 性能考慮:數據權限過濾會增加SQL復雜度,可能影響查詢性能,特別是對于大數據量表。可以考慮添加適當的索引優化。

        2. SQL注入風險:在拼接SQL時要特別注意防止SQL注入,建議使用預編譯參數。

        3. 緩存問題:如果使用了緩存,需要注意數據權限可能導致緩存命中率下降或數據泄露問題。

        4. 多租戶場景:在多租戶系統中,數據權限通常需要與租戶隔離一起考慮。

        5. 復雜查詢:對于復雜的多表關聯查詢,數據權限條件可能需要更精細的控制。

        通過以上方案,我們可以靈活地在MyBatis-Plus中實現各種數據權限控制需求,根據項目實際情況選擇最適合的實現方式。

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

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

        相關文章

        大模型——Dify 與 Browser-use 結合使用

        大模型——Dify 與 Browser-use 結合使用 Dify 與 Browser-use 的結合使用,能夠通過 AI 決策與自動化交互的協同,構建智能化、場景化的業務流程。 以下是兩者的整合思路與技術落地方案: 一、核心組合邏輯 分工定位 Dify:作為AI模型調度中樞,負責自然語言理解、決策生成、…

        transformer demo

        import torch import torch.nn as nn import torch.nn.functional as F import math import numpy as np import pytestclass PositionalEncoding(nn.Module):def __init__(self, d_model, max_seq_length5000):super(PositionalEncoding, self).__init__()# 創建位置編碼矩陣p…

        centos 8.3(阿里云服務器)mariadb由系統自帶版本(10.3)升級到10.6

        1. 備份數據庫 在進行任何升級操作前&#xff0c;務必備份所有數據庫&#xff1a; mysqldump -u root -p --all-databases > all_databases_backup.sql # 或者為每個重要數據庫單獨備份 mysqldump -u root -p db_name1 > db_name1_backup.sql mysqldump -u root -p db…

        如何穩定地更新你的大模型知識(算法篇)

        目錄 在線強化學習的穩定知識獲取機制:算法優化與數據策略一、算法層面的穩定性控制機制二、數據處理策略的穩定性保障三、訓練過程中的漸進式優化策略四、環境設計與反饋機制的穩定性影響五、穩定性保障的綜合應用策略六、總結與展望通過強化學習來讓大模型學習高層語義知識,…

        圖的遍歷模板

        圖的遍歷 BFS 求距離 #include<bits/stdc.h>using namespace std;int n, m, k,q[20001],dist[20001]; vector<int> edge[20001];int main(){scanf("%d%d%d",&n,&m,&k);for (int i 1;i<m;i){int x,y;scanf("%d%d",&x,&am…

        Java集合 - LinkedList底層源碼解析

        以下是基于 JDK 8 的 LinkedList 深度源碼解析&#xff0c;涵蓋其數據結構、核心方法實現、性能特點及使用場景。我們從 類結構、Node節點、插入/刪除/訪問操作、線程安全、性能對比 等角度進行詳細分析 一、類結構與繼承關系 1. 類定義 public class LinkedList<E> e…

        Pytorch 卷積神經網絡參數說明一

        系列文章目錄 文章目錄 系列文章目錄前言一、卷積層的定義1.常見的卷積操作2. 感受野3. 如何理解參數量和計算量4.如何減少計算量和參數量 二、神經網絡結構&#xff1a;有些層前面文章說過&#xff0c;不全講1. 池化層&#xff08;下采樣&#xff09;2. 上采樣3. 激活層、BN層…

        C++ 中的 iostream 庫:cin/cout 基本用法

        iostream 是 C 標準庫中用于輸入輸出操作的核心庫&#xff0c;它基于面向對象的設計&#xff0c;提供了比 C 語言的 stdio.h 更強大、更安全的 I/O 功能。下面詳細介紹 iostream 庫中最常用的輸入輸出工具&#xff1a;cin 和 cout。 一、 基本概念 iostream 庫&#xff1a;包…

        SAP復制一個自定義移動類型

        SAP復制移動類型 在SAP系統中&#xff0c;復制移動類型201可以通過事務碼OMJJ或SPRO路徑完成&#xff0c;用于創建自定義的移動類型以滿足特定業務需求。 示例操作步驟 進入OMJJ事務碼&#xff1a; 打開事務碼OMJJ&#xff0c;選擇“移動類型”選項。 復制移動類型&#xff…

        Bambu Studio 中的“回抽“與“裝填回抽“的區別

        回抽 裝填回抽: Bambu Studio 中的“回抽” (Retraction) 和“裝填回抽”(Prime/Retract) 是兩個不同的概念&#xff0c;它們都與材料擠出機的操作過程相關&#xff0c;但作用和觸發條件有所不同。 回抽(Retraction): 回抽的作用, 在打印機移動到另一個位置之前&#xff0c;將…

        危化品安全監測數據分析挖掘范式:從被動響應到戰略引擎的升維之路

        在危化品生產的復雜生態系統中,安全不僅僅是合規性要求,更是企業生存和發展的生命線。傳統危化品安全生產風險監測預警系統雖然提供了基礎保障,但其“事后響應”和“單點預警”的局限性日益凸顯。我們正處在一個由大數據、人工智能、數字孿生和物聯網技術驅動的范式變革前沿…

        C++ RPC 遠程過程調用詳細解析

        一、RPC 基本原理 RPC (Remote Procedure Call) 是一種允許程序調用另一臺計算機上子程序的協議,而不需要程序員顯式編碼這個遠程交互細節。其核心思想是使遠程調用看起來像本地調用一樣。 RPC 工作流程 客戶端調用:客戶端調用本地存根(stub)方法參數序列化:客戶端存根將參…

        Python:操作 Excel 預設色

        ??親愛的技術愛好者們,熱烈歡迎來到 Kant2048 的博客!我是 Thomas Kant,很開心能在CSDN上與你們相遇~?? 本博客的精華專欄: 【自動化測試】 【測試經驗】 【人工智能】 【Python】 Python 操作 Excel 系列 讀取單元格數據按行寫入設置行高和列寬自動調整行高和列寬水平…

        中科院1區|IF10+:加大醫學系團隊利用GPT-4+電子病歷分析,革新肝硬化并發癥隊列識別

        中科院1區|IF10&#xff1a;加大醫學系團隊利用GPT-4電子病歷分析&#xff0c;革新肝硬化并發癥隊列識別 在當下的科研領域&#xff0c;人工智能尤其是大語言模型的迅猛發展&#xff0c;正為各個學科帶來前所未有的機遇與變革。在醫學范疇&#xff0c;從疾病的早期精準篩查&am…

        Python學習小結

        bg&#xff1a;記錄一下&#xff0c;怕忘了&#xff1b;先寫一點&#xff0c;后面再補充。 1、沒有方法重載 2、字段都是公共字段 3、都是類似C#中頂級語句的寫法 4、對類的定義直接&#xff1a; class Student: 創建對象不需要new關鍵字&#xff0c;直接stu Student() 5、方…

        QCustomPlot 中實現拖動區域放大?與恢復

        1、拖動區域放大? 在 QCustomPlot 中實現 ?拖動區域放大?&#xff08;即通過鼠標左鍵拖動繪制矩形框選區域進行放大&#xff09;的核心方法是設置 SelectionRectMode。具體操作步驟&#xff1a; 1?&#xff09;禁用拖動模式? 確保先關閉默認的圖表拖動功能&#xff08;否…

        如何將文件從 iPhone 傳輸到閃存驅動器

        您想將文件從 iPhone 或 iPad 傳輸到閃存盤進行備份嗎&#xff1f;這是一個很好的決定&#xff0c;但您需要先了解一些實用的方法。雖然 Apple 生態系統在很大程度上是封閉的&#xff0c;但您可以使用一些實用工具將文件從 iPhone 或 iPad 傳輸到閃存盤。下文提供了這些行之有效…

        互聯網大廠Java求職面試:云原生架構與微服務設計中的復雜挑戰

        互聯網大廠Java求職面試&#xff1a;云原生架構與微服務設計中的復雜挑戰 面試官開場白 面試官&#xff08;嚴肅模式開啟&#xff09;&#xff1a;鄭薪苦&#xff0c;歡迎來到我們的技術面試環節。我是本次面試的技術總監&#xff0c;接下來我們將圍繞云原生架構、微服務設計、…

        leetcode-hot-100 (鏈表)

        1. 相交鏈表 題目鏈接&#xff1a;相交鏈表 題目描述&#xff1a;給你兩個單鏈表的頭節點 headA 和 headB &#xff0c;請你找出并返回兩個單鏈表相交的起始節點。如果兩個鏈表不存在相交節點&#xff0c;返回 null 。 解答&#xff1a; 其實這道題目我一開始沒太看懂題目給…

        Web前端基礎之HTML

        一、瀏覽器 火狐瀏覽器、谷歌瀏覽器(推薦)、IE瀏覽器 推薦谷歌瀏覽器原因&#xff1a; 1、簡潔大方,打開速度快 2、開發者調試工具&#xff08;右鍵空白處->檢查&#xff0c;打開調試模式&#xff09; 二、開發工具 核心IDE工具 Visual Studio Code (VS Code)? 微軟開發…