最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis攔截器實現(xiàn)數(shù)據(jù)權(quán)限詳解

 更新時間:2023年11月23日 09:55:29   作者:chaojunma  
這篇文章主要介紹了Mybatis攔截器實現(xiàn)數(shù)據(jù)權(quán)限詳解, 通過Mybatis攔截器我們可以攔截某些方法的調(diào)用,我們可以選擇在這些被攔截的方法執(zhí)行前后加上某些邏輯,需要的朋友可以參考下

前言

在我們?nèi)粘i_發(fā)過程中,通常會涉及到數(shù)據(jù)權(quán)限問題,下面以我們常見的一種場景舉例:

一個公司有很多部門,每個人所處的部門和角色也不同,所以數(shù)據(jù)權(quán)限也可能不同,比如超級管理員可以查看某張表的素有信息,部門領(lǐng)導(dǎo)可以查看該部門下的相關(guān)信息,部門普通人員只可以查看個人相關(guān)信息,而且由于角色的不同,各個角色所能查看到的數(shù)據(jù)庫字段也可能不相同,那么此處就涉及到了數(shù)據(jù)權(quán)限相關(guān)的問題。

那么我們該如何處理數(shù)據(jù)權(quán)限相關(guān)的問題呢?

我們提供一種通過Mybatis攔截器實現(xiàn)的方式,下面我們來具體實現(xiàn)一下

具體實現(xiàn)

pom.xml依賴

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.13.RELEASE</version>
</parent>
 
<properties>
    <java.version>1.8</java.version>
    <mybatis-plus.version>3.2.0</mybatis-plus.version>
</properties>
 
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>${mybatis-plus.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

application.yml文件

server:
  port: 80
 
spring:
  application:
    name: data-scope
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
    druid:
      # 驗證連接是否有效。此參數(shù)必須設(shè)置為非空字符串,下面三項設(shè)置成true才能生效
      validation-query: SELECT 1
      # 連接是否被空閑連接回收器(如果有)進行檢驗. 如果檢測失敗, 則連接將被從池中去除
      test-while-idle: true
      # 是否在從池中取出連接前進行檢驗, 如果檢驗失敗, 則從池中去除連接并嘗試取出另一個
      test-on-borrow: true
      # 是否在歸還到池中前進行檢驗
      test-on-return: false
      # 連接在池中最小生存的時間,單位是毫秒
      min-evictable-idle-time-millis: 30000
 
#mybatis配置
mybatis-plus:
  type-aliases-package: com.mk.entity
  mapper-locations: classpath:mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      field-strategy: not_empty
      logic-delete-value: 1
      logic-not-delete-value: 0
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
    call-setters-on-nulls: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

代碼實現(xiàn)

DataScode.java

@Data
public class DataScope {
 
    // sql過濾條件
    String sqlCondition;
 
    // 需要過濾的結(jié)果字段
    String[] filterFields;
 
}

MybatisPlusConfig.java

@Configuration
public class MybatisPlusConfig {
 
    @Bean
    @ConditionalOnMissingBean
    public DataScopeInterceptor dataScopeInterceptor() {
        return new DataScopeInterceptor();
    }
 
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor page = new PaginationInterceptor();
        page.setDialectType(DbType.MYSQL.getDb());
        return page;
    }
 
    @Bean
    public ConfigurationCustomizer mybatisConfigurationCustomizer(){
        return new ConfigurationCustomizer() {
            @Override
            public void customize(MybatisConfiguration configuration) {
                configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory());
            }
        };
    }
}

DataScopeInterceptor.java

@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "query",
        args = {MappedStatement.class, Object.class, RowBounds.class,ResultHandler.class})})
public class DataScopeInterceptor implements Interceptor {
 
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
 
        log.info("執(zhí)行intercept方法:{}", invocation.toString());
 
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameterObject = args[1];
 
        // 查找參數(shù)中包含DataScope類型的參數(shù)
        DataScope dataScope = findDataScopeObject(parameterObject);
        if (dataScope == null) {
            return invocation.proceed();
        }
 
 
        if (!ObjectUtils.isEmpty(dataScope.getSqlCondition())) {
            // id為執(zhí)行的mapper方法的全路徑名,如com.mapper.UserMapper
            String id = ms.getId();
 
            // sql語句類型 select、delete、insert、update
            String sqlCommandType = ms.getSqlCommandType().toString();
 
            // 僅攔截 select 查詢
            if (!sqlCommandType.equals(SqlCommandType.SELECT.toString())) {
                return invocation.proceed();
            }
 
            BoundSql boundSql = ms.getBoundSql(parameterObject);
            String origSql = boundSql.getSql();
            log.info("原始SQL: {}", origSql);
 
            // 組裝新的 sql
            String newSql = String.format("%s%s%s%s", "select * from (", origSql, ") ", dataScope.getSqlCondition());
 
            // 重新new一個查詢語句對象
            BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), newSql,
                    boundSql.getParameterMappings(), boundSql.getParameterObject());
 
            // 把新的查詢放到statement里
            MappedStatement newMs = newMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
            for (ParameterMapping mapping : boundSql.getParameterMappings()) {
                String prop = mapping.getProperty();
                if (boundSql.hasAdditionalParameter(prop)) {
                    newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
                }
            }
 
            Object[] queryArgs = invocation.getArgs();
            queryArgs[0] = newMs;
 
            log.info("改寫的SQL: {}", newSql);
        }
 
        Object result = invocation.proceed();
 
        return handleReslut(result, Arrays.asList(dataScope.getFilterFields()));
    }
 
    /**
     * 定義一個內(nèi)部輔助類,作用是包裝 SQL
     */
    class BoundSqlSqlSource implements SqlSource {
        private BoundSql boundSql;
        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }
        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
 
    }
 
    private MappedStatement newMappedStatement (MappedStatement ms, SqlSource newSqlSource) {
        MappedStatement.Builder builder = new
                MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {
            builder.keyProperty(ms.getKeyProperties()[0]);
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
 
    @Override
    public Object plugin(Object target) {
        log.info("plugin方法:{}", target);
 
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
 
    }
 
    @Override
    public void setProperties(Properties properties) {
        // 獲取屬性
        // String value1 = properties.getProperty("prop1");
        log.info("properties方法:{}", properties.toString());
    }
 
 
 
    /**
     * 查找參數(shù)是否包括DataScope對象
     *
     * @param parameterObj 參數(shù)列表
     * @return DataScope
     */
    private DataScope findDataScopeObject(Object parameterObj) {
        if (parameterObj instanceof DataScope) {
            return (DataScope) parameterObj;
        } else if (parameterObj instanceof Map) {
            for (Object val : ((Map<?, ?>) parameterObj).values()) {
                if (val instanceof DataScope) {
                    return (DataScope) val;
                }
            }
        }
        return null;
    }
 
 
    public Object handleReslut(Object returnValue, List<String> filterFields){
        if(returnValue != null && !ObjectUtils.isEmpty(filterFields)){
            if (returnValue instanceof ArrayList<?>){
                List<?> list = (ArrayList<?>) returnValue;
                List<Object> newList  = new ArrayList<Object>();
                if (1 <= list.size()) {
                    for(Object object:list){
                        if (object instanceof Map) {
                            Map map = (Map) object;
                            for (String key : filterFields) {
                                map.remove(key);
                            }
                            newList.add(map);
                        } else {
                            newList.add(decrypt(filterFields, object));
                        }
                    }
                    returnValue = newList;
                }
            } else {
                if (returnValue instanceof Map) {
                    Map map = (Map) returnValue;
                    for (String key : filterFields) {
                        map.remove(key);
                    }
                } else {
                    returnValue = decrypt(filterFields, returnValue);
                }
            }
        }
        return returnValue;
    }
 
 
    public static <T> T decrypt(List<String> filterFields, T t) {
        Field[] declaredFields = t.getClass().getDeclaredFields();
        try {
            if (declaredFields != null && declaredFields.length > 0) {
                for (Field field : declaredFields) {
                    if (filterFields.contains(field.getName())) {
                        field.setAccessible(true);
                        field.set(t, null);
                        field.setAccessible(false);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
 
        return t;
    }
}

SalariesMapper.xml

<mapper namespace="com.mk.mapper.SalariesMapper">
    <select id="pageList" resultType="com.mk.entity.Salaries">
        SELECT * from salaries where salary between #{start} and #{end}
    </select>
 
    <select id="getByEmpNo" resultType="java.util.Map">
        select * from salaries where emp_no = #{empNo} limit 0,1
    </select>
</mapper>

SalariesMapper.java

@Mapper
public interface SalariesMapper extends BaseMapper<Salaries> {
 
    List<Salaries> pageList(DataScope dataScope, @Param("start") int start,  @Param("end") int end, Page<Salaries> page);
 
    Map<String, Object> getByEmpNo(DataScope dataScope, @Param("empNo") int empNo);
}

SalariesService.java

@Service
public class SalariesService extends ServiceImpl<SalariesMapper, Salaries> {
 
    @Autowired
    private SalariesMapper salariesMapper;
 
    public List<Salaries> getList(){
        Page<Salaries> page = new Page<>(1, 10);
        DataScope dataScope = new DataScope();
        // 設(shè)置查詢條件
        dataScope.setSqlCondition("s where 1=1 and s.emp_no = '10001'");
        // 將結(jié)果集過濾掉salary和toDate字段
        dataScope.setFilterFields(new String[]{"salary", "toDate"});
        return salariesMapper.pageList(dataScope, 60000, 70000, page);
 
    }
 
    public Map<String, Object> getByEmpNo() {
        DataScope dataScope = new DataScope();
        // 將結(jié)果集過濾掉salary和toDate字段
        dataScope.setFilterFields(new String[]{"salary", "toDate"});
        return salariesMapper.getByEmpNo(dataScope, 10001);
    }
}

啟動服務(wù),執(zhí)行相關(guān)操作,sql在執(zhí)行之前會執(zhí)行DataScopeInterceptor攔截器中的邏輯,從而改變sql,具體的相關(guān)操作就是將原來的sql語句origSql在外層包裝一層過濾條件,如:select * from (origSql) 過濾條件,此處的過濾條件要封裝到DataScope對象中

例如:

dataScope.setSqlCondition("s where 1=1 and s.emp_no = '10001'") 

那么在經(jīng)過攔截器處理以后要執(zhí)行的sql語句為

select * from (origSql) s where 1=1 and s.emp_no = '10001'

從而實現(xiàn)數(shù)據(jù)權(quán)限相操作,當(dāng)然此處的過濾條件只是為了演示效果舉的一個例子

而已,在實際開發(fā)過程中要根據(jù)用戶角色等等設(shè)置具體的過濾條件。

到此這篇關(guān)于Mybatis攔截器實現(xiàn)數(shù)據(jù)權(quán)限詳解的文章就介紹到這了,更多相關(guān)Mybatis攔截器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot下Mybatis的緩存的實現(xiàn)步驟

    SpringBoot下Mybatis的緩存的實現(xiàn)步驟

    這篇文章主要介紹了SpringBoot下Mybatis的緩存的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • SpringSecurity Demo 創(chuàng)建項目的過程詳解

    SpringSecurity Demo 創(chuàng)建項目的過程詳解

    本文介紹了使用SpringBoot 3.2.4創(chuàng)建一個簡單的安全驗證項目的過程,包括引入依賴、配置文件修改、創(chuàng)建控制器和啟動類等步驟,并最終成功啟動項目并在瀏覽器訪問到請求接口,感興趣的朋友跟隨小編一起看看吧
    2026-04-04
  • Java8新特性Stream的完全使用指南

    Java8新特性Stream的完全使用指南

    這篇文章主要給大家介紹了關(guān)于Java8新特性Stream的完全使用指南,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Java8具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • SpringBoot項目找不到接口報404錯誤的解決辦法

    SpringBoot項目找不到接口報404錯誤的解決辦法

    寫了一個簡單的springboot項目,在啟動的時候idea未報錯,瀏覽器訪問接口時報404的錯誤,所以本文給大家介紹了SpringBoot項目找不到接口報404錯誤的解決辦法,文中有相關(guān)的圖文供大家參考,需要的朋友可以參考下
    2024-12-12
  • SpringBoot參數(shù)校驗之@Validated的使用詳解

    SpringBoot參數(shù)校驗之@Validated的使用詳解

    這篇文章主要通過示例為大家詳細(xì)介紹一下介紹了SpringBoot參數(shù)校驗中@Validated的使用方法,文中的示例代碼講解詳細(xì),需要的可以參考一下
    2022-06-06
  • logback標(biāo)記日志過濾器MarkerFilter源碼解讀

    logback標(biāo)記日志過濾器MarkerFilter源碼解讀

    這篇文章主要為大家介紹了logback標(biāo)記日志過濾器MarkerFilter源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-11-11
  • 在項目中集成jetty server步驟解析

    在項目中集成jetty server步驟解析

    這篇文章主要介紹了在項目中集成jetty server步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-02-02
  • Java如何使用Set接口存儲沒有重復(fù)元素的數(shù)組

    Java如何使用Set接口存儲沒有重復(fù)元素的數(shù)組

    Set是一個繼承于Collection的接口,即Set也是集合中的一種。Set是沒有重復(fù)元素的集合,本篇我們就用它存儲一個沒有重復(fù)元素的數(shù)組
    2022-04-04
  • Java內(nèi)存模型的深入講解

    Java內(nèi)存模型的深入講解

    這篇文章主要給大家介紹了關(guān)于Java內(nèi)存模型的相關(guān)資料,我們常說的JVM內(nèi)存模式指的是JVM的內(nèi)存分區(qū),而Java內(nèi)存模式是一種虛擬機規(guī)范,需要的朋友可以參考下
    2021-07-07
  • Java利用Spire.Doc for Java實現(xiàn)Word文檔轉(zhuǎn)換為常見圖像格式

    Java利用Spire.Doc for Java實現(xiàn)Word文檔轉(zhuǎn)換為常見圖像格式

    在現(xiàn)代軟件開發(fā)中,尤其是在構(gòu)建企業(yè)級應(yīng)用和內(nèi)容管理系統(tǒng)時,處理 Word 文檔是家常便飯,本文將深入探討如何利用 Java 高效、高質(zhì)量地將 Word 文檔轉(zhuǎn)換為 JPG、PNG 和 SVG 等主流圖片格式,大家可以根據(jù)需要進行選擇
    2025-10-10

最新評論

曲阜市| 柳州市| 靖远县| 安泽县| 通化市| 长宁县| 赤峰市| 渑池县| 仁寿县| 视频| 秭归县| 屏山县| 慈利县| 泸溪县| 嘉峪关市| 丰城市| 固安县| 灵台县| 麻江县| 象山县| 北辰区| 习水县| 西平县| 北京市| 泰州市| 河曲县| 酒泉市| 喀什市| 界首市| 两当县| 石楼县| 禄劝| 南丰县| 清水县| 大悟县| 盘山县| 徐汇区| 海盐县| 东源县| 桐乡市| 阳高县|