目录

利用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

# 解读源码

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

# 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

# 注意点

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

# 注解设置

因为我都是用的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

# 加入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
上次更新: 2024-01-03, 13:22:13
最近更新
01
2023年度总结
01-03
02
MongoDB的简单的常用语法
12-11
03
cetnos7通过nfs共享磁盘文件
11-24
更多文章>