博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
系统数据权限的实现方案
阅读量:7117 次
发布时间:2019-06-28

本文共 4565 字,大约阅读时间需要 15 分钟。

系统数据权限的实现方案

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

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

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

数据库表定义如下;

T_sys_table_dependence

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底层次的统一处理的封装问题,很好地解决了系统对数据权限的要求。  

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

     本文转自 gaochaojs 51CTO博客,原文链接:http://blog.51cto.com/jncumter/1608556,如需转载请自行联系原作者
你可能感兴趣的文章
shell问题-报错即退出
查看>>
Bug解决过程复盘
查看>>
JavaScript和HTML DOM的区别与联系
查看>>
行为型设计模式之模板方法(Template Method)
查看>>
C#收发邮件(Jmail+Professional+v4.4+特别版)
查看>>
T-SQL事务编写
查看>>
CGZip, a C++ wrapper for gzip methods
查看>>
将NavigationBar设置透明
查看>>
Go Revel - Results(响应)
查看>>
c# WinForm 边框阴影窗体
查看>>
主线程不能直接调用webservice方法
查看>>
Redis集群方案之使用豌豆荚Codis搭建(待实践)
查看>>
Universal USB Installer – Easy as 1 2 3
查看>>
假期小结 BIO, NIO, AIO
查看>>
Jvm(57),类加载器----初次认识加载器
查看>>
poj1053
查看>>
【关键帧提取】基于运动信息(续3)
查看>>
pygame学习笔记(1)——安装及矩形、圆型画图
查看>>
jQuery UI Portlet 1.1.2 发布
查看>>
Intellij Idea @Autowired取消提示
查看>>