热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Mybatis拦截器实现数据权限的示例代码

在我们日常开发过程中,通常会涉及到数据权限问题,本文主要介绍了Mybatis拦截器实现数据权限的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的

Mybatis拦截器实现数据权限的示例代码

在我们日常开发过程中,通常会涉及到数据权限问题,下面以我们常见的一种场景举例:

一个公司有很多部门,每个人所处的部门和角色也不同,所以数据权限也可能不同,比如超级管理员可以查看某张

表的素有信息,部门领导可以查看该部门下的相关信息,部门普通人员只可以查看个人相关信息,而且由于角色的

不同,各个角色所能查看到的数据库字段也可能不相同,那么此处就涉及到了数据权限相关的问题。那么我们该如

何处理数据权限相关的问题呢?我们提供一种通过Mybatis拦截器实现的方式,下面我们来具体实现一下

pom.xml依赖


    org.springframework.boot
    spring-boot-starter-parent
    2.1.13.RELEASE

 

    1.8
    3.2.0

 

    
        org.springframework.boot
        spring-boot-starter-web
    
    
        mysql
        mysql-connector-java
    
    
        com.baomidou
        mybatis-plus-boot-starter
        ${mybatis-plus.version}
    
    
        org.projectlombok
        lombok
    

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:
      # 验证连接是否有效。此参数必须设置为非空字符串,下面三项设置成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

代码实现

DataScode.java

@Data
public class DataScope {
 
    // sql过滤条件
    String sqlCondition;
 
    // 需要过滤的结果字段
    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("执行intercept方法:{}", invocation.toString());
 
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        Object parameterObject = args[1];
 
        // 查找参数中包含DataScope类型的参数
        DataScope dataScope = findDataScopeObject(parameterObject);
        if (dataScope == null) {
            return invocation.proceed();
        }
 
 
        if (!ObjectUtils.isEmpty(dataScope.getSqlCondition())) {
            // id为执行的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()));
    }
 
    /**
     * 定义一个内部辅助类,作用是包装 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());
    }
 
 
 
    /**
     * 查找参数是否包括DataScope对象
     *
     * @param parameterObj 参数列表
     * @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 filterFields){
        if(returnValue != null && !ObjectUtils.isEmpty(filterFields)){
            if (returnValue instanceof ArrayList){
                List list = (ArrayList) returnValue;
                List newList  = new ArrayList();
                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 decrypt(List 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


    
 
    

SalariesMapper.java

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

SalariesService.java

@Service
public class SalariesService extends ServiceImpl {
 
    @Autowired
    private SalariesMapper salariesMapper;
 
    public List getList(){
        Page page = new Page<>(1, 10);
        DataScope dataScope = new DataScope();
        // 设置查询条件
        dataScope.setSqlCondition("s where 1=1 and s.emp_no = "10001"");
        // 将结果集过滤掉salary和toDate字段
        dataScope.setFilterFields(new String[]{"salary", "toDate"});
        return salariesMapper.pageList(dataScope, 60000, 70000, page);
 
    }
 
    public Map getByEmpNo() {
        DataScope dataScope = new DataScope();
        // 将结果集过滤掉salary和toDate字段
        dataScope.setFilterFields(new String[]{"salary", "toDate"});
        return salariesMapper.getByEmpNo(dataScope, 10001);
    }
}

启动服务,执行相关操作,sql在执行之前会执行DataScopeInterceptor拦截器中的逻辑,从而改变sql,具体的

相关操作就是将原来的sql语句origSql在外层包装一层过滤条件,如:select * from (origSql) 过滤条件,

此处的过滤条件要封装到DataScope对象中,例如:dataScope.setSqlCondition("s where 1=1 and

s.emp_no = &#39;10001&#39;") , 那么在经过拦截器处理以后要执行的sql语句为

select * from (origSql) s where 1=1 and s.emp_no = &#39;10001&#39;, 从而实现数据权限相操作,当然此处的过滤条件只是为了演示效果举的一个例子

而已,在实际开发过程中要根据用户角色等等设置具体的过滤条件。

到此这篇关于Mybatis拦截器实现数据权限的示例代码的文章就介绍到这了,更多相关Mybatis拦截器实现数据权限内容请搜索编程笔记以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程笔记!


推荐阅读
  • kettle 8.3 连接mysql8.0.30 的入坑体验
      写这个东西主要是想吐槽为主,真的无语了。  kettle是java写的java版权是oracle的。mysql是Oracle的。   吐槽:  一般来说,在kettle中新建m ... [详细]
  • 1.使用通用mapper时实体类的mapper接口(即普通的dao接口)继承了通用mapper接口后,在依赖注入实体类的mapper接口时 ... [详细]
  • 基于SSL的mysql服务器的主从架构实现说明:本文选用172.16.22.1作为主服务器,172.16.22.3作为从服务器从服务器的mysql软件版 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • Activiti7流程定义开发笔记
    本文介绍了Activiti7流程定义的开发笔记,包括流程定义的概念、使用activiti-explorer和activiti-eclipse-designer进行建模的方式,以及生成流程图的方法。还介绍了流程定义部署的概念和步骤,包括将bpmn和png文件添加部署到activiti数据库中的方法,以及使用ZIP包进行部署的方式。同时还提到了activiti.cfg.xml文件的作用。 ... [详细]
  • Python操作MySQL(pymysql模块)详解及示例代码
    本文介绍了使用Python操作MySQL数据库的方法,详细讲解了pymysql模块的安装和连接MySQL数据库的步骤,并提供了示例代码。内容涵盖了创建表、插入数据、查询数据等操作,帮助读者快速掌握Python操作MySQL的技巧。 ... [详细]
  • 如何搭建服务器环境php(2023年最新解答)
    导读:本篇文章编程笔记来给大家介绍有关如何搭建服务器环境php的相关内容,希望对大家有所帮助,一起来看看吧。本文目录一览:1、怎么搭建p ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
author-avatar
GuangLi1472_716
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有