Mybatis-Plus⼊门系列(3)-MybatisPlus之数据权限插件DataPer。。。
前⾔
在plus3.4.2版本中,阅读源码时发现了⼀个新插件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.Id()))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){
PlainSelect plainSelect =(PlainSelect) SelectBody();
Expression sqlSegment = Where(),(String) obj);
if(null != sqlSegment){
plainSelect.setWhere(sqlSegment);
}
}
}
源码分析
1. 继承抽象类JsqlParserSupport并重写processSelect⽅法。JSqlParser是⼀个SQL语句解析器,它将SQL转换为Java类的可遍历
层次结构。plus中也引⼊了JSqlParser包,processSelect可以对Select语句进⾏处理。
2. 实现InnerInterceptor接⼝并重写beforeQuery⽅法。InnerInterceptor是plus的插件接⼝,beforeQuery可以对查询语句执⾏前进
⾏处理。
3. DataPermissionHandler作为数据权限处理器,是⼀个接⼝,提供getSqlSegment⽅法添加数据权限 SQL ⽚段。
4. 由上可知,我们只需要实现DataPermissionHandler接⼝,并按照业务规则处理SQL,就可以实现数据权限的功能。
/**
* 数据权限处理器
*
* @author hubin
* @since 3.4.1 +
*/
public interface DataPermissionHandler {
/**
*
* @param where 待执⾏ SQL Where 条件表达式
* @param mappedStatementId Mybatis MappedStatement Id 根据该参数可以判断具体执⾏⽅法
* @return JSqlParser 条件表达式
*/
sql语句查询结果取反Expression getSqlSegment(Expression where, String mappedStatementId);
}
使⽤案例
实现同⼀个查询接⼝,本门经理只能查看当前部门的所有数据,⽼板则能查看所有部门数据类似的功能,员⼯则只能查看⾃⼰数据的数据权限功能。
1. 创建部门枚举类
@Getter
@AllArgsConstructor
@NoArgsConstructor
public enum DeptEnum {
BOOS(1,"⽼总"),
MANAGER_01(2,"01部门经理"),
MANAGER_06(6,"06部门经理");
int type;
String desc;
}
2. 表及实体类添加部门编号
3. 编写MyDataPermissionHandler类,继承DataPermissionHandler,编写数据权限处理逻辑,对SQL进⾏拦截处理
@Slf4j
public class MyDataPermissionHandler implements DataPermissionHandler {
/**
* @param where 原SQL Where 条件表达式
* @param mappedStatementId Mapper接⼝⽅法ID
* @return
*/
@SneakyThrows
@Override
public Expression getSqlSegment(Expression where, String mappedStatementId){
log.info("=========================== start MyDataPermissionHandler");
// 1. 模拟获取登录⽤户,从⽤户信息中获取部门ID
Random random =new Random();
int userDeptId = Int(9)+1;// 随机部门ID 1-10 随机数
Expression expression = null;
log.info("=============== userDeptId:{}", userDeptId);
if(userDeptId == Type()){
// 2.userDeptId为1,说明是⽼总,可查看所有数据⽆需处理
return where;
}else if(userDeptId == DeptEnum.Type()){
// 3. userDeptId为2,说明是02部门经理,可查看02部门及下属部门所有数据
/
/ 创建IN 表达式
Set<String> deptIds = wLinkedHashSet();// 创建IN范围的元素集合
deptIds.add("2");
deptIds.add("3");
deptIds.add("4");
deptIds.add("5");
ItemsList itemsList =new ExpressionList(deptIds.stream().map(StringValue::new).List()));// 把集合转变为JSQLParser需要的元素列表
InExpression inExpression =new InExpression(new Column("order_tbl.dept_id"), itemsList);// order_tbl.dept_id IN ('2', '3', '4', '5') return new AndExpression(where, inExpression);
}else if(userDeptId == DeptEnum.Type()){
/
/ 4. userDeptId为6,说明是06部门经理,可查看06部门及下属部门所有数据
// 创建IN 表达式
Set<String> deptIds = wLinkedHashSet();// 创建IN范围的元素集合
deptIds.add("6");
deptIds.add("7");
deptIds.add("8");
deptIds.add("9");
ItemsList itemsList =new ExpressionList(deptIds.stream().map(StringValue::new).List()));// 把集合转变为JSQLParser需要的元素列表
InExpression inExpression =new InExpression(new Column("order_tbl.dept_id"), itemsList);
return new AndExpression(where, inExpression);
}else{
// 5. userDeptId为其他时,表⽰为员⼯级别没有下属机构,只能查看当前部门的数据
// = 表达式
EqualsTo equalsTo =new EqualsTo();// order_tbl.dept_id = userDeptId
equalsTo.setLeftExpression(new Column("order_tbl.dept_id"));
equalsTo.setRightExpression(new LongValue(userDeptId));
// 创建 AND 表达式拼接Where 和 = 表达式
return new AndExpression(where, equalsTo);// WHERE user_id = 2 AND order_tbl.dept_id = 3
}
}
}
4. 配置类添加数据权限插件
// 添加数据权限插件
DataPermissionInterceptor dataPermissionInterceptor=new DataPermissionInterceptor();
MyDataPermissionHandler myDataPermissionHandler=new MyDataPermissionHandler();
// 添加⾃定义的数据权限处理器
dataPermissionInterceptor.setDataPermissionHandler(myDataPermissionHandler);
interceptor.addInnerInterceptor(dataPermissionInterceptor);
案例测试
1. 当部门ID为1时,查询出来所有数据
2. 当部门ID为最下层机构时,只能查询到当前部门的数据
3. 当为部门经理时,查到看当前及下属所有机构的数据
4. 综上,通过简单的案例测试,能完成数据权限部分功能,可根据此深⼊,⽐如更深层次的按照⽤户、⾓⾊等绑定数据权限。
忽略拦截
plus提供了 @InterceptorIgnore⽅法对插件进⾏是否拦截配置,官⽹:该注解作⽤于 xxMapper.java ⽅法之上 各属性代表对应的插件 各属性不给值则默认为 false 设置为 true。既然此注解只能作⽤于Mapper,看来需要⾃⼰写⼀个注解,做⽤于service层,实现接⼝级别的拦截配置。。。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface InterceptorIgnore {
/**
* ⾏级租户 {@link sion.plugins.inner.TenantLineInnerInterceptor}
*/
String tenantLine()default"";
/**
* 动态表名 {@link sion.plugins.inner.DynamicTableNameInnerInterceptor}
*/
String dynamicTableName()default"";
/**
* 攻击 SQL 阻断解析器,防⽌全表更新与删除 {@link sion.plugins.inner.BlockAttackInnerInterceptor}
*/
String blockAttack()default"";
/**
* 垃圾SQL拦截 {@link sion.plugins.inner.IllegalSQLInnerInterceptor
}
*/
String illegalSql()default"";
/**
* 数据权限 {@link sion.plugins.inner.DataPermissionInterceptor}
* <p>
* 默认关闭,需要注解打开
*/
String dataPermission()default"1";
/**
* 分表 {@link sion.plugins.inner.ShardingInnerInterceptor}
*/
String sharding()default"";
/**
* 其他的
* <p>
* 格式应该为: "key"+"@"+可选项[false,true,1,0,on,off]
* 例如: "xxx@1" 或 "xxx@true" 或 "xxx@on"
* <p>
* 如果配置了该属性的注解是注解在 Mapper 上的,则如果该 Mapper 的⼀部分 Method 需要取反则需要在 Method 上注解并配置此属性为反值 * 例如: "xxx@1" 在 Mapper 上, 则 Method 上需要 "xxx@0"
*/
String[]others()default{};
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论