利用mybatis-plus实现数据权限
# 前言
一般使用sql的拦截器搭配aop来实现,最近想使用mybatis-plus来实现一下
# mybatis-plus的DataPermissionInterceptor和DataPermissionHandler
mybatis-plus有一个使用JsqlParser的DataPermissionInterceptor,但是不知出于什么原因在官网上并没有提供文档,需要使用mp3.4.1以上的版本。
# DataPermissionInterceptor源码
/**
* 数据权限处理器
*
* @author hubin
* @since 3.4.1 +
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@SuppressWarnings({"rawtypes"})
public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
private DataPermissionHandler dataPermissionHandler;
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
if (InterceptorIgnoreHelper.willIgnoreDataPermission(ms.getId())) return;
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
mpBs.sql(parserSingle(mpBs.sql(), ms.getId()));
}
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
SelectBody selectBody = select.getSelectBody();
if (selectBody instanceof PlainSelect) {
this.setWhere((PlainSelect) selectBody, (String) obj);
} else if (selectBody instanceof SetOperationList) {
SetOperationList setOperationList = (SetOperationList) selectBody;
List<SelectBody> selectBodyList = setOperationList.getSelects();
selectBodyList.forEach(s -> this.setWhere((PlainSelect) s, (String) obj));
}
}
/**
* 设置 where 条件
*
* @param plainSelect 查询对象
* @param whereSegment 查询条件片段
*/
protected void setWhere(PlainSelect plainSelect, String whereSegment) {
Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), whereSegment);
if (null != sqlSegment) {
plainSelect.setWhere(sqlSegment);
}
}
}
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 解读源码
DataPermissionInterceptor中有个setWhere方法,我们可以看到这个方法体中调用了dataPermissionHandler,同时传参where和whereSegment。接下来我们再看一下DataPermissionHandler
# DataPermissionHandler
/**
* 数据权限处理器
*
* @author hubin
* @since 3.4.1 +
*/
public interface DataPermissionHandler {
/**
* 获取数据权限 SQL 片段
*
* @param where 待执行 SQL Where 条件表达式
* @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法
* @return JSqlParser 条件表达式
*/
Expression getSqlSegment(Expression where, String mappedStatementId);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# DataPermissionHandler解读
DataPermissionHandler其实就是个interface,提供了一个getSqlSegment方法。那我们只需要自己实现一下这个接口是不是就能实现?尝试一下
# 实现DataPermissionHandler
* 数据权限
*
* @author teler
* @date 2022-02-16
*/
@Slf4j
public class MyDataPermissionHandler implements DataPermissionHandler {
/**
* 获取数据权限 SQL 片段
*
* @param where 待执行 SQL Where 条件表达式
* @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执行方法
*
* @return JSqlParser 条件表达式
*/
@Override
public Expression getSqlSegment(Expression where, String mappedStatementId) {
log.info("数据权限处理器===========");
log.info("{}",
where);
log.info("{}",
mappedStatementId);
log.info("是否忽略:{}",
InterceptorIgnoreHelper.willIgnoreDataPermission(mappedStatementId));
//没登录、没有权限直接返回原文。我这里用了sa-token,请自行更改
if (!StpUtil.isLogin()) {
return where;
}
//这地方用了sa-token,自行看返回值变成自己的
List<String> permissionList = StpUtil.getPermissionList();
if (permissionList.size() <= 0) {
return where;
}
// 把集合转变为 JSQLParser需要的元素列表
ItemsList itemsList = new ExpressionList(permissionList.stream()
.map(StringValue::new)
.collect(Collectors.toList()));
InExpression inExpression = new InExpression(new Column("group_id"),
itemsList);
/*
如果where为null,sql会出现
where null and group_id in(1,2,3)
的情况,这里处理一下手动赋值sql为
where id > Long.MIN_VALUE and group_id in (1,2,3)
*/
if (null == where) {
//设置where
GreaterThan greaterThan = new GreaterThan();
greaterThan.setLeftExpression(new Column("id"));
greaterThan.setRightExpression(new LongValue(Long.MIN_VALUE));
where = greaterThan;
}
//添加and sql
return new AndExpression(where,
inExpression);
}
}
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 注意点
mapper方法中有个注解@InterceptorIgnore(dataPermission = "true")
mp文档 (opens new window)中是这么解释的
该注解作用于 xxMapper.java 方法之上 各属性代表对应的插件 各属性不给值则默认为 false 设置为 true 忽略拦截 更多说明详见源码注释
我们去看一下InterceptorIgnore的源码会发现关于权限的属性是默认关闭的。结合官方文档的解释,那么我们需要在要使用数据权限的地方打开这个注解
/**
* 数据权限 {@link com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor}
* <p>
* 默认关闭,需要注解打开
*/
String dataPermission() default "1";
1
2
3
4
5
6
2
3
4
5
6
# 注解设置
因为我都是用的mp的单表查询功能,所以直接Override
/**
* 用户
*
* @author Teler
*/
@Mapper
@Component
@InterceptorIgnore(dataPermission = "true")
public interface UserMapper extends BaseMapper<UserEntity> {
/**
* 根据 ID 查询
*
* @param id 主键ID
*/
@Override
@InterceptorIgnore(dataPermission = "true")
UserEntity selectById(Serializable id);
/**
* 根据 entity 条件,查询全部记录
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
@Override
@InterceptorIgnore(dataPermission = "false")
List<UserEntity> selectList(@Param(Constants.WRAPPER) Wrapper<UserEntity> queryWrapper);
/**
* 根据 entity 条件,查询全部记录(并翻页)
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
@Override
@InterceptorIgnore(dataPermission = "false")
<P extends IPage<UserEntity>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<UserEntity> queryWrapper);
}
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
30
31
32
33
34
35
36
37
38
39
40
41
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
30
31
32
33
34
35
36
37
38
39
40
41
# 加入mp的插件
提示
注意插件顺序,官网文档推荐顺序为
- 多租户,动态表名
- 分页,乐观锁
- sql 性能规范,防止全表更新与删除
虽然文档中没提到,但是我们需要把数据权限放到前面,防止sql顺序不对
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 数据权限
DataPermissionInterceptor dataPermissionInterceptor = new DataPermissionInterceptor();
dataPermissionInterceptor.setDataPermissionHandler(new MyDataPermissionHandler());
interceptor.addInnerInterceptor(dataPermissionInterceptor);
//分页
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
return interceptor;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
编辑 (opens new window)
上次更新: 2024-11-06, 19:27:10