系统数据权限的实现方案

系统数据权限的实现方案


  目前正在开发的OA系统希望实现这样一个需求:每个使用系统的用户具有某种基于工号和组织架构的权限,主要区分为可以查看个人、查看本部门或指定部门以及查看所有。这样的权限控制称之为数据范围。

    对于数据范围的实现主要通过以下方案:

1.配置数据依赖表,数据依赖表中有以下字段:主表的空间、主表、主表别称、依赖表、依赖表别名

数据库表定义如下;

T_sys_table_dependence


wKiom1TGEzuTCrIRAAE--SpHqsk443.jpg

2.mybatis.xml配置拦截器。Mybatis所谓的拦截器的一个作用就是可以拦截某些方法的调用,可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法。Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。比如在本方案中即可根据当前用户的数据范围动态的组装成sql作为所有查询sql的附加逻辑。Mybatis拦截器需要实现其接口Interceptor,其接口如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
package  org.apache.ibatis.plugin;
 
import  java.util.Properties;
 
public  interface  Interceptor {
 
   Object intercept(Invocation invocation)  throws  Throwable;
 
   Object plugin(Object target);
 
   void  setProperties(Properties properties);
 
}

    通过该接口定义,可以看出实现一个mybatis拦截器最重要的是实现三述三个接口方法。Intercept方法定义拦截的时候需要实现的逻辑;plugin方法决定是否拦截以及返回的目标对象;而setProperties则是将拦截器配置中的参数信息映射进来。

  本方案中对该接口的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  Object intercept(Invocation invocation)  throws  Throwable {
     Object[] args = invocation.getArgs();
     MappedStatement ms = (MappedStatement) args[ 0 ];
     Object parameter = args[ 1 ];
     RowBounds rowBounds = (RowBounds) args[ 2 ];
     int  offset = rowBounds.getOffset();
     int  limit = rowBounds.getLimit();
     List<TabledependenceEntity> list = DataRuleConstants.CACHE_TABLEDEPENDENCEMAP.get(ms.getId());
     if (!CollectionUtils.isEmpty(list)){
         BoundSql boundSql = ms.getBoundSql(parameter);
         String sql = boundSql.getSql().trim();
         //add data rule:yourself logic
         sql = dataruleHandler.addConditionToSql(sql, list);
         BoundSql newBoundSql =  new  BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
         copyMetaParameters(boundSql, newBoundSql);
         MappedStatement newMs = copyFromMappedStatement(ms,  new  BoundSqlSqlSource(newBoundSql));
         args[ 0 ] = newMs;
     }
     return  invocation.proceed();
}

  另外,mybatis拦截器还有两个注解特别重要,一个是@Intercepts,其值是一个@Signature数组。@Intercepts用于表明当前的对象是一个Interceptor,而@Signature则表明要拦截的接口、方法以及对应的参数类型。本应用中配置如下:

1
2
@Intercepts ({ @Signature (type = Executor. class , method =  "query" , args = { 
     MappedStatement. class , Object. class , RowBounds. class , ResultHandler. class  }) })

  以上配置的含义是:当Executor代理对象在执行参数类型为MappedStatement、Object、RowBounds和ResultHandler的query方法时,拦截器就会进行拦截,而对其他的请求则直接返回代理对象,而非目标对象。

3.通过配置Mybatis.xml注册拦截器,配置如下:

1
2
3
< plugin  interceptor = "com.fx.oa.module.com.dependence.server.service.impl.DataRuleIntercept" >
             < property  name = "dataruleHandler"  value = "com.fx.oa.module.com.dependence.server.service.impl.DataruleHandler" />
         </ plugin >

  通过以上三个步骤,即实现了一个mybatis的拦截器。

4.下面将会对数据范围实现的逻辑进行介绍,即本文第2段落的内容  

1
//add data rule:yourself logic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
private  String filterSpecialValue(String field, String value) {
         if  ( "deptCode" .equals(field)){
             if ( "1" .equals(value)){
                 return  null ;
             }
             if (DataRuleConstants.VALUE_CURRENTDEPTCODE.equals(value)){
                 value = OAUserContext.getOrgCode();
             }
             //获取当前部门的所有子部门
             orgService = (IOrgService) SpringContextUtil.getApplicationContext().getBean( "orgService" );
             String[] valus = value.split( "," );
             List<String> orgList =  new  ArrayList<String>();
             for (String val : valus){
                 if (StringUtils.isNotEmpty(val)){
                     orgList.addAll(orgService.queryOrgChildCode(val));
                 }
             }
             if (!CollectionUtils.isEmpty(orgList)){
                 for (String org : orgList){
                 value +=  ","  + org;
                 }
             }
         } else  if ( "userCode" .equals(field)){
             if (DataRuleConstants.VALUE_CURRENTUSERCODE.equals(value)){
                 value = OAUserContext.getUserCode();
             }
         }
         return  value;
     }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  String addConditionToSql(String sql, List<TabledependenceEntity> list) {
         TabledependenceEntity td = list.get( 0 );
         String mybatisId = td.getMybatisId();
         String lowerSql = sql.toLowerCase();
         StringBuffer sqlString =  new  StringBuffer();
         List<DataruleEntity> ruleList = getRelatedDatarule(sql, td);
         if (!CollectionUtils.isEmpty(ruleList)){
             String searchTable = td.getTableName();
             String whereSql = lowerSql.split(DataRuleConstants.SQL_ORDERBY)[ 0 ] +  " " ;
             String orderSql =  "" ;
             boolean  containsOrder = lowerSql.contains(DataRuleConstants.SQL_ORDERBY);
             if (containsOrder){
                 orderSql =  " "  + DataRuleConstants.SQL_ORDERBY + lowerSql.split(DataRuleConstants.SQL_ORDERBY)[ 1 ];
             }
             StringBuffer generatedSql = generateDynamicSql(lowerSql, ruleList, td);
             return  (sqlString.append(whereSql).append(generatedSql).append(orderSql)).toString();
         } else  {
             return  sql;
         }
     }

    通过mybatis拦截器为所有查询请求添加并执行额外逻辑,较优雅地实现了DAO底层次的统一处理的封装问题,很好地解决了系统对数据权限的要求。  

更多关于拦截器方面的知识,可以参考以下文章:


Mybatis拦截器介绍及分页插件


MYBATIS 配置文件(转载)




     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1608556,如需转载请自行联系原作者


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值