Commit e9385219c21eee812f5fa3541a141e8cce316447

Authored by YunaiV
1 parent eda2b11d

数据权限的逻辑处理,暂未测试

yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptor.java
@@ -4,18 +4,20 @@ import cn.hutool.core.collection.CollUtil; @@ -4,18 +4,20 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.util.collection.SetUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
5 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule; 5 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
6 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory; 6 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
  7 +import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
7 import com.alibaba.ttl.TransmittableThreadLocal; 8 import com.alibaba.ttl.TransmittableThreadLocal;
8 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; 9 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
9 import com.baomidou.mybatisplus.core.toolkit.PluginUtils; 10 import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
10 -import com.baomidou.mybatisplus.core.toolkit.StringPool;  
11 import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport; 11 import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
12 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; 12 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
13 import lombok.RequiredArgsConstructor; 13 import lombok.RequiredArgsConstructor;
14 import net.sf.jsqlparser.expression.*; 14 import net.sf.jsqlparser.expression.*;
15 import net.sf.jsqlparser.expression.operators.conditional.AndExpression; 15 import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
16 import net.sf.jsqlparser.expression.operators.conditional.OrExpression; 16 import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
17 -import net.sf.jsqlparser.expression.operators.relational.*;  
18 -import net.sf.jsqlparser.schema.Column; 17 +import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
  18 +import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
  19 +import net.sf.jsqlparser.expression.operators.relational.InExpression;
  20 +import net.sf.jsqlparser.expression.operators.relational.ItemsList;
19 import net.sf.jsqlparser.schema.Table; 21 import net.sf.jsqlparser.schema.Table;
20 import net.sf.jsqlparser.statement.delete.Delete; 22 import net.sf.jsqlparser.statement.delete.Delete;
21 import net.sf.jsqlparser.statement.select.*; 23 import net.sf.jsqlparser.statement.select.*;
@@ -32,6 +34,15 @@ import java.sql.Connection; @@ -32,6 +34,15 @@ import java.sql.Connection;
32 import java.util.*; 34 import java.util.*;
33 import java.util.concurrent.ConcurrentHashMap; 35 import java.util.concurrent.ConcurrentHashMap;
34 36
  37 +/**
  38 + * 数据权限拦截器,通过 {@link DataPermissionRule} 数据权限规则,重写 SQL 的方式来实现
  39 + * 主要的 SQL 重写方法,可见 {@link #builderExpression(Expression, Table)} 方法
  40 + *
  41 + * 整体的代码实现上,参考 {@link com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor} 实现。
  42 + * 所以每次 MyBatis Plus 升级时,需要 Review 下其具体的实现是否有变更!
  43 + *
  44 + * @author 芋道源码
  45 + */
35 @RequiredArgsConstructor 46 @RequiredArgsConstructor
36 public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor { 47 public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
37 48
@@ -40,7 +51,8 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -40,7 +51,8 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
40 private final MappedStatementCache mappedStatementCache = new MappedStatementCache(); 51 private final MappedStatementCache mappedStatementCache = new MappedStatementCache();
41 52
42 @Override // SELECT 场景 53 @Override // SELECT 场景
43 - public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 54 + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter,
  55 + RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
44 // 获得 Mapper 对应的数据权限的规则 56 // 获得 Mapper 对应的数据权限的规则
45 List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId()); 57 List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId());
46 if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过 58 if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
@@ -59,12 +71,12 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -59,12 +71,12 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
59 } 71 }
60 } 72 }
61 73
62 - @Override // 只处理 UPDATE / DELETE 场景 74 + @Override // 只处理 UPDATE / DELETE 场景,不处理 INSERT 场景
63 public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) { 75 public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
64 PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh); 76 PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
65 MappedStatement ms = mpSh.mappedStatement(); 77 MappedStatement ms = mpSh.mappedStatement();
66 SqlCommandType sct = ms.getSqlCommandType(); 78 SqlCommandType sct = ms.getSqlCommandType();
67 - if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { // 无需处理 Insert 语句 79 + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
68 // 获得 Mapper 对应的数据权限的规则 80 // 获得 Mapper 对应的数据权限的规则
69 List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId()); 81 List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(ms.getId());
70 if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过 82 if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
@@ -117,7 +129,8 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -117,7 +129,8 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
117 @Override 129 @Override
118 protected void processUpdate(Update update, int index, String sql, Object obj) { 130 protected void processUpdate(Update update, int index, String sql, Object obj) {
119 final Table table = update.getTable(); 131 final Table table = update.getTable();
120 - update.setWhere(this.andExpression(table, update.getWhere())); 132 +// update.setWhere(this.andExpression(table, update.getWhere()));
  133 + update.setWhere(this.builderExpression(update.getWhere(), table));
121 } 134 }
122 135
123 /** 136 /**
@@ -125,26 +138,27 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -125,26 +138,27 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
125 */ 138 */
126 @Override 139 @Override
127 protected void processDelete(Delete delete, int index, String sql, Object obj) { 140 protected void processDelete(Delete delete, int index, String sql, Object obj) {
128 - delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere())); 141 +// delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
  142 + delete.setWhere(this.builderExpression(delete.getWhere(), delete.getTable()));
129 } 143 }
130 144
131 - /**  
132 - * delete update 语句 where 处理  
133 - */  
134 - protected BinaryExpression andExpression(Table table, Expression where) {  
135 - //获得where条件表达式  
136 - EqualsTo equalsTo = new EqualsTo();  
137 - equalsTo.setLeftExpression(this.getAliasColumn(table));  
138 - equalsTo.setRightExpression(getTenantId());  
139 - if (null != where) {  
140 - if (where instanceof OrExpression) {  
141 - return new AndExpression(equalsTo, new Parenthesis(where));  
142 - } else {  
143 - return new AndExpression(equalsTo, where);  
144 - }  
145 - }  
146 - return equalsTo;  
147 - } 145 +// /**
  146 +// * delete update 语句 where 处理
  147 +// */
  148 +// protected BinaryExpression andExpression(Table table, Expression where) {
  149 +// //获得where条件表达式
  150 +// EqualsTo equalsTo = new EqualsTo();
  151 +// equalsTo.setLeftExpression(this.getAliasColumn(table));
  152 +// equalsTo.setRightExpression(getTenantId());
  153 +// if (null != where) {
  154 +// if (where instanceof OrExpression) {
  155 +// return new AndExpression(equalsTo, new Parenthesis(where));
  156 +// } else {
  157 +// return new AndExpression(equalsTo, where);
  158 +// }
  159 +// }
  160 +// return equalsTo;
  161 +// }
148 162
149 /** 163 /**
150 * 处理 PlainSelect 164 * 处理 PlainSelect
@@ -155,10 +169,11 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -155,10 +169,11 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
155 processWhereSubSelect(where); 169 processWhereSubSelect(where);
156 if (fromItem instanceof Table) { 170 if (fromItem instanceof Table) {
157 Table fromTable = (Table) fromItem; 171 Table fromTable = (Table) fromItem;
158 - if (!ignoreTable(fromTable.getName())) {  
159 - //#1186 github  
160 - plainSelect.setWhere(builderExpression(where, fromTable));  
161 - } 172 +// if (!ignoreTable(fromTable.getName())) {
  173 +// //#1186 github
  174 +// plainSelect.setWhere(builderExpression(where, fromTable));
  175 +// }
  176 + plainSelect.setWhere(builderExpression(where, fromTable));
162 } else { 177 } else {
163 processFromItem(fromItem); 178 processFromItem(fromItem);
164 } 179 }
@@ -311,20 +326,21 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -311,20 +326,21 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
311 processJoin(join); 326 processJoin(join);
312 continue; 327 continue;
313 } 328 }
314 - // 当前表是否忽略  
315 - boolean needIgnore = ignoreTable(fromTable.getName());  
316 - // 表名压栈,忽略的表压入 null,以便后续不处理  
317 - tables.push(needIgnore ? null : fromTable); 329 +// // 当前表是否忽略
  330 +// boolean needIgnore = ignoreTable(fromTable.getName());
  331 +// // 表名压栈,忽略的表压入 null,以便后续不处理
  332 +// tables.push(needIgnore ? null : fromTable);
318 // 尾缀多个 on 表达式的时候统一处理 333 // 尾缀多个 on 表达式的时候统一处理
319 if (originOnExpressions.size() > 1) { 334 if (originOnExpressions.size() > 1) {
320 Collection<Expression> onExpressions = new LinkedList<>(); 335 Collection<Expression> onExpressions = new LinkedList<>();
321 for (Expression originOnExpression : originOnExpressions) { 336 for (Expression originOnExpression : originOnExpressions) {
322 Table currentTable = tables.poll(); 337 Table currentTable = tables.poll();
323 - if (currentTable == null) {  
324 - onExpressions.add(originOnExpression);  
325 - } else {  
326 - onExpressions.add(builderExpression(originOnExpression, currentTable));  
327 - } 338 +// if (currentTable == null) {
  339 +// onExpressions.add(originOnExpression);
  340 +// } else {
  341 +// onExpressions.add(builderExpression(originOnExpression, currentTable));
  342 +// }
  343 + onExpressions.add(builderExpression(originOnExpression, currentTable));
328 } 344 }
329 join.setOnExpressions(onExpressions); 345 join.setOnExpressions(onExpressions);
330 } 346 }
@@ -341,15 +357,18 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -341,15 +357,18 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
341 protected void processJoin(Join join) { 357 protected void processJoin(Join join) {
342 if (join.getRightItem() instanceof Table) { 358 if (join.getRightItem() instanceof Table) {
343 Table fromTable = (Table) join.getRightItem(); 359 Table fromTable = (Table) join.getRightItem();
344 - if (ignoreTable(fromTable.getName())) {  
345 - // 过滤退出执行  
346 - return;  
347 - } 360 +// if (ignoreTable(fromTable.getName())) {
  361 +// // 过滤退出执行
  362 +// return;
  363 +// }
348 // 走到这里说明 on 表达式肯定只有一个 364 // 走到这里说明 on 表达式肯定只有一个
349 - Collection<Expression> originOnExpressions = join.getOnExpressions();  
350 - List<Expression> onExpressions = new LinkedList<>();  
351 - onExpressions.add(builderExpression(originOnExpressions.iterator().next(), fromTable));  
352 - join.setOnExpressions(onExpressions); 365 +// Collection<Expression> originOnExpressions = join.getOnExpressions();
  366 +// List<Expression> onExpressions = new LinkedList<>();
  367 +// onExpressions.add(builderExpression(originOnExpressions.iterator().next(), fromTable));
  368 +// join.setOnExpressions(onExpressions);
  369 + Expression originOnExpression = CollUtil.getFirst(join.getOnExpressions());
  370 + originOnExpression = builderExpression(originOnExpression, fromTable);
  371 + join.setOnExpressions(CollUtil.newArrayList(originOnExpression));
353 } 372 }
354 } 373 }
355 374
@@ -357,50 +376,69 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne @@ -357,50 +376,69 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
357 * 处理条件 376 * 处理条件
358 */ 377 */
359 protected Expression builderExpression(Expression currentExpression, Table table) { 378 protected Expression builderExpression(Expression currentExpression, Table table) {
360 - EqualsTo equalsTo = new EqualsTo();  
361 - equalsTo.setLeftExpression(this.getAliasColumn(table));  
362 - equalsTo.setRightExpression(getTenantId()); 379 + // 获得 Table 对应的数据权限条件
  380 + Expression equalsTo = buildDataPermissionExpression(table);
  381 + if (equalsTo == null) { // 如果没条件,则返回 currentExpression 默认
  382 + return currentExpression;
  383 + }
  384 +
  385 + // 表达式为空,则直接返回 equalsTo
363 if (currentExpression == null) { 386 if (currentExpression == null) {
364 return equalsTo; 387 return equalsTo;
365 } 388 }
  389 + // 如果表达式为 Or,则需要 (currentExpression) AND equalsTo
366 if (currentExpression instanceof OrExpression) { 390 if (currentExpression instanceof OrExpression) {
367 return new AndExpression(new Parenthesis(currentExpression), equalsTo); 391 return new AndExpression(new Parenthesis(currentExpression), equalsTo);
368 - } else {  
369 - return new AndExpression(currentExpression, equalsTo);  
370 } 392 }
  393 + // 如果表达式为 And,则直接返回 currentExpression AND equalsTo
  394 + return new AndExpression(currentExpression, equalsTo);
371 } 395 }
372 396
  397 +// /**
  398 +// * 租户字段别名设置
  399 +// * <p>tenantId 或 tableAlias.tenantId</p>
  400 +// *
  401 +// * @param table 表对象
  402 +// * @return 字段
  403 +// */
  404 +// protected Column getAliasColumn(Table table) {
  405 +// StringBuilder column = new StringBuilder();
  406 +// if (table.getAlias() != null) {
  407 +// column.append(table.getAlias().getName()).append(StringPool.DOT);
  408 +// }
  409 +// column.append(getTenantIdColumn());
  410 +// return new Column(column.toString());
  411 +// }
  412 +
373 /** 413 /**
374 - * 租户字段别名设置  
375 - * <p>tenantId 或 tableAlias.tenantId</p> 414 + * 构建指定表的数据权限的 Expression 过滤条件
376 * 415 *
377 - * @param table 表对象  
378 - * @return 字段 416 + * @param table 表
  417 + * @return Expression 过滤条件
379 */ 418 */
380 - protected Column getAliasColumn(Table table) {  
381 - StringBuilder column = new StringBuilder();  
382 - if (table.getAlias() != null) {  
383 - column.append(table.getAlias().getName()).append(StringPool.DOT); 419 + private Expression buildDataPermissionExpression(Table table) {
  420 + // 生成条件
  421 + Expression allExpression = null;
  422 + for (DataPermissionRule rule : ContextHolder.getRules()) {
  423 + // 判断表名是否匹配
  424 + if (!rule.getTableNames().contains(table.getName())) {
  425 + continue;
  426 + }
  427 + // 单条规则的条件
  428 + String tableName = MyBatisUtils.getTableName(table);
  429 + Expression oneExpress = rule.getExpression(tableName, table.getAlias());
  430 + // 拼接到 allExpression 中
  431 + allExpression = allExpression == null ? oneExpress
  432 + : new AndExpression(allExpression, oneExpress);
384 } 433 }
385 - column.append(getTenantIdColumn());  
386 - return new Column(column.toString());  
387 - }  
388 -  
389 - // TODO 芋艿:未实现  
390 434
391 - private boolean ignoreTable(String tableName) {  
392 - return false;  
393 - }  
394 -  
395 - private String getTenantIdColumn() {  
396 - return "dept_id";  
397 - }  
398 -  
399 - private Expression getTenantId() {  
400 - return new LongValue(1L); 435 + // 如果条件非空,说明已经重写了
  436 + if (allExpression != null) {
  437 + ContextHolder.setRewrite(true);
  438 + }
  439 + return allExpression;
401 } 440 }
402 441
403 -  
404 /** 442 /**
405 * 判断 SQL 是否重写。如果没有重写,则添加到 {@link MappedStatementCache} 中 443 * 判断 SQL 是否重写。如果没有重写,则添加到 {@link MappedStatementCache} 中
406 * 444 *
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.OrderItem; @@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.core.metadata.OrderItem;
7 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; 7 import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
8 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; 8 import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
9 import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 9 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  10 +import net.sf.jsqlparser.schema.Table;
10 11
11 import java.util.ArrayList; 12 import java.util.ArrayList;
12 import java.util.Collection; 13 import java.util.Collection;
@@ -18,6 +19,8 @@ import java.util.stream.Collectors; @@ -18,6 +19,8 @@ import java.util.stream.Collectors;
18 */ 19 */
19 public class MyBatisUtils { 20 public class MyBatisUtils {
20 21
  22 + private static final String MYSQL_ESCAPE_CHARACTER = "`";
  23 +
21 public static <T> Page<T> buildPage(PageParam pageParam) { 24 public static <T> Page<T> buildPage(PageParam pageParam) {
22 return buildPage(pageParam, null); 25 return buildPage(pageParam, null);
23 } 26 }
@@ -48,4 +51,20 @@ public class MyBatisUtils { @@ -48,4 +51,20 @@ public class MyBatisUtils {
48 interceptor.setInterceptors(inners); 51 interceptor.setInterceptors(inners);
49 } 52 }
50 53
  54 + /**
  55 + * 获得 Table 对应的表名
  56 + *
  57 + * 兼容 MySQL 转义表名 `t_xxx`
  58 + *
  59 + * @param table 表
  60 + * @return 去除转移字符后的表名
  61 + */
  62 + public static String getTableName(Table table) {
  63 + String tableName = table.getName();
  64 + if (tableName.startsWith(MYSQL_ESCAPE_CHARACTER) && tableName.endsWith(MYSQL_ESCAPE_CHARACTER)) {
  65 + tableName = tableName.substring(1, tableName.length() - 1);
  66 + }
  67 + return tableName;
  68 + }
  69 +
51 } 70 }