系统数据权限的实现方案

简介:

系统数据权限的实现方案


  目前正在开发的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,如需转载请自行联系原作者


相关文章
|
3月前
|
数据安全/隐私保护
用户及组管理操作
用户及组管理操作
20 0
|
10月前
|
安全 数据安全/隐私保护
产品权限分析与设计
产品权限分析与设计
163 0
|
11月前
|
SQL XML 缓存
修改若依的数据权限功能
修改若依的数据权限功能
961 0
|
数据安全/隐私保护
9-企业权限管理-用户操作
9-企业权限管理-用户操作
9-企业权限管理-用户操作
|
存储 弹性计算 监控
日志审计多帐号采集方案升级--资源目录集成
阿里云SLS日志服务集成阿里云资源目录服务,支持跨账号实时自动化、中心化采集阿里云产品日志。
359 0
日志审计多帐号采集方案升级--资源目录集成
|
SQL 存储 数据库
数据权限这样设计,你觉得如何?
在项目实际开发中我们不光要控制一个用户能访问哪些资源,还需要控制用户只能访问资源中的某部分数据。 控制一个用户能访问哪些资源我们有很成熟的权限管理模型即RBAC,但是控制用户只能访问某部分资源(即我们常说的数据权限)使用RBAC模型是不够的,本文我们尝试在RBAC模型的基础上融入数据权限的管理控制。
2807 1
|
关系型数据库 数据库
通用数据级别权限的框架设计与实现(2)-数据权限的准备工作
查看上篇文章通用数据级别权限的框架设计(1)-相关业务场景的分析",我们要继续做一些准备工作。 我们先要设置当前用户信息的类 /** * @description: 用户对象 * @author: starmark * @create: 2...
847 0