mybatis 数据权限插件,mybatis拦截器

  mybatis 数据权限插件,mybatis拦截器

  在我们的日常开发过程中,通常会涉及到数据权限。下面是一个常见场景的示例:

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

  表称为信息表,部门领导可以查看部门下的相关信息,部门普通人只能查看个人信息,并且由于角色

  不同角色可以查看的不同数据库字段也可能不同,所以这里涉及到数据权限相关的问题。那我们应该像

  如何处理与数据权利相关的问题?我们提供了一种通过Mybatis拦截器实现它的方法。来具体实施一下吧。

  Pom.xml依赖项

  父groupIdorg.springframework.boot/groupId artifactId spring-boot-starter-parent/artifactId version2.1.13.RELEASE/version/父属性Java . version 1.8/Java . version mybatis-plus . version 3 . 2 . 0/mybatis-plus . version/properties依赖关系groupIdorg.springframework.boot/groupId artifactId spring-boot-starter-web/artifactId/依赖关系groupId MySQL/groupId artifactId MySQL-connector-Java/artifactId/依赖关系groupIdcom.baomidou/groupId artifactId mybatis-plus-boot-starter/artifactId version $ { mybatis-plus .

  server : port : 80 spring : application : name : data-scope data source : URL : JDBC : my SQL ://localhost :3306/test?unicode=true character encoding=UTF-8 usssl=false server time zone=UTC用户名3360Root密码3360123456Druid3360 #验证连接是否有效。此参数必须设置为非空字符串,并且以下三项必须设置为true才能生效。验证-空闲连接收集器(如果有)检查query: SELECT 1 #连接。如果测试失败,连接将从池中删除。Test-while-idle 3360 true #在从池中取出连接之前是否进行了检查?如果测试失败,请从池中删除连接并尝试取出另一个test-on-borrow3360 true #在将连接返回到池之前是否要检查Test-on-return3360 false #池中连接的最短生命周期,单位为毫秒min-evidentable-idle-time-mills 3360 30000 # Mybatis configures Mybatis-plus 3: type-aliases-package :cm . MK . entitymapper-locations 3360 class path 333XML global-config 3360 db-config 3360 id-type 3360 autofield-strategy 3360 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

  

@Datapublic class DataScope { // sql过滤条件 String sqlCondition; // 需要过滤的结果字段 String[] filterFields; }

MybatisPlusConfig.java

 

  

@Configurationpublic 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<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

 

  

@Mapperpublic 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

 

  

@Servicepublic 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(); // 设置查询条件 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<String, Object> 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 = '10001'") , 那么在经过拦截器处理以后要执行的sql语句为

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

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

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

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: