Commit 814c2db65c72ee7dfa1b056d2eaf383cb9e99c00

Authored by YunaiV
1 parent 4b95146c

初始化数据权限模块

yudao-dependencies/pom.xml
... ... @@ -24,7 +24,7 @@
24 24 <!-- DB 相关 -->
25 25 <mysql.version>5.1.46</mysql.version>
26 26 <druid.version>1.2.4</druid.version>
27   - <mybatis-plus.version>3.4.2</mybatis-plus.version>
  27 + <mybatis-plus.version>3.4.3.4</mybatis-plus.version>
28 28 <dynamic-datasource.version>3.3.2</dynamic-datasource.version>
29 29 <redisson.version>3.16.3</redisson.version>
30 30 <!-- Config 配置中心相关 -->
... ...
yudao-framework/pom.xml
... ... @@ -33,7 +33,7 @@
33 33 <module>yudao-spring-boot-starter-biz-weixin</module>
34 34 <module>yudao-spring-boot-starter-extension</module>
35 35 <module>yudao-spring-boot-starter-tenant</module>
36   - <module>yudao-spring-boot-starter-datascope</module>
  36 + <module>yudao-spring-boot-starter-data-permission</module>
37 37 </modules>
38 38  
39 39 <artifactId>yudao-framework</artifactId>
... ...
yudao-framework/yudao-spring-boot-starter-datascope/pom.xml renamed to yudao-framework/yudao-spring-boot-starter-data-permission/pom.xml
... ... @@ -8,7 +8,7 @@
8 8 <version>${revision}</version>
9 9 </parent>
10 10 <modelVersion>4.0.0</modelVersion>
11   - <artifactId>yudao-spring-boot-starter-datascope</artifactId>
  11 + <artifactId>yudao-spring-boot-starter-data-permission</artifactId>
12 12 <packaging>jar</packaging>
13 13  
14 14 <name>${artifactId}</name>
... ... @@ -21,40 +21,12 @@
21 21 <artifactId>yudao-common</artifactId>
22 22 </dependency>
23 23  
24   - <!-- Web 相关 -->
25   - <dependency>
26   - <groupId>org.springframework.boot</groupId>
27   - <artifactId>spring-boot-starter-web</artifactId>
28   - </dependency>
29   -
30   - <dependency>
31   - <groupId>cn.iocoder.boot</groupId>
32   - <artifactId>yudao-spring-boot-starter-security</artifactId>
33   - </dependency>
34   -
35 24 <!-- DB 相关 -->
36 25 <dependency>
37 26 <groupId>cn.iocoder.boot</groupId>
38 27 <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
39 28 </dependency>
40 29  
41   - <dependency>
42   - <groupId>cn.iocoder.boot</groupId>
43   - <artifactId>yudao-spring-boot-starter-redis</artifactId>
44   - </dependency>
45   -
46   - <!-- Job 定时任务相关 -->
47   - <dependency>
48   - <groupId>cn.iocoder.boot</groupId>
49   - <artifactId>yudao-spring-boot-starter-job</artifactId>
50   - </dependency>
51   -
52   - <!-- 消息队列相关 -->
53   - <dependency>
54   - <groupId>cn.iocoder.boot</groupId>
55   - <artifactId>yudao-spring-boot-starter-mq</artifactId>
56   - </dependency>
57   -
58 30 <!-- Test 测试相关 -->
59 31 <dependency>
60 32 <groupId>org.springframework.boot</groupId>
... ...
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datascope/core/interceptor/DataPermissionInterceptor.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.datascope.core.interceptor;
  2 +
  3 +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
  4 +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
  5 +import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
  6 +import com.baomidou.mybatisplus.core.toolkit.StringPool;
  7 +import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
  8 +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
  9 +import net.sf.jsqlparser.expression.*;
  10 +import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
  11 +import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
  12 +import net.sf.jsqlparser.expression.operators.relational.*;
  13 +import net.sf.jsqlparser.schema.Column;
  14 +import net.sf.jsqlparser.schema.Table;
  15 +import net.sf.jsqlparser.statement.delete.Delete;
  16 +import net.sf.jsqlparser.statement.select.*;
  17 +import net.sf.jsqlparser.statement.update.Update;
  18 +import org.apache.ibatis.executor.Executor;
  19 +import org.apache.ibatis.executor.statement.StatementHandler;
  20 +import org.apache.ibatis.mapping.BoundSql;
  21 +import org.apache.ibatis.mapping.MappedStatement;
  22 +import org.apache.ibatis.mapping.SqlCommandType;
  23 +import org.apache.ibatis.session.ResultHandler;
  24 +import org.apache.ibatis.session.RowBounds;
  25 +
  26 +import java.sql.Connection;
  27 +import java.sql.SQLException;
  28 +import java.util.Collection;
  29 +import java.util.Deque;
  30 +import java.util.LinkedList;
  31 +import java.util.List;
  32 +
  33 +public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
  34 +
  35 +// private TenantLineHandler tenantLineHandler;
  36 +
  37 + @Override
  38 + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  39 + if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) return;
  40 + PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
  41 + mpBs.sql(parserSingle(mpBs.sql(), null));
  42 + }
  43 +
  44 + @Override
  45 + public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
  46 + PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
  47 + MappedStatement ms = mpSh.mappedStatement();
  48 + SqlCommandType sct = ms.getSqlCommandType();
  49 + if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { // 无需处理 Insert 语句
  50 + if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) return;
  51 + PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
  52 + mpBs.sql(parserMulti(mpBs.sql(), null));
  53 + }
  54 + }
  55 +
  56 + @Override
  57 + protected void processSelect(Select select, int index, String sql, Object obj) {
  58 + processSelectBody(select.getSelectBody());
  59 + List<WithItem> withItemsList = select.getWithItemsList();
  60 + if (!CollectionUtils.isEmpty(withItemsList)) {
  61 + withItemsList.forEach(this::processSelectBody);
  62 + }
  63 + }
  64 +
  65 + protected void processSelectBody(SelectBody selectBody) {
  66 + if (selectBody == null) {
  67 + return;
  68 + }
  69 + if (selectBody instanceof PlainSelect) {
  70 + processPlainSelect((PlainSelect) selectBody);
  71 + } else if (selectBody instanceof WithItem) {
  72 + WithItem withItem = (WithItem) selectBody;
  73 + processSelectBody(withItem.getSubSelect().getSelectBody());
  74 + } else {
  75 + SetOperationList operationList = (SetOperationList) selectBody;
  76 + List<SelectBody> selectBodys = operationList.getSelects();
  77 + if (CollectionUtils.isNotEmpty(selectBodys)) {
  78 + selectBodys.forEach(this::processSelectBody);
  79 + }
  80 + }
  81 + }
  82 +
  83 + /**
  84 + * update 语句处理
  85 + */
  86 + @Override
  87 + protected void processUpdate(Update update, int index, String sql, Object obj) {
  88 + final Table table = update.getTable();
  89 + if (ignoreTable(table.getName())) {
  90 + // 过滤退出执行
  91 + return;
  92 + }
  93 + update.setWhere(this.andExpression(table, update.getWhere()));
  94 + }
  95 +
  96 + /**
  97 + * delete 语句处理
  98 + */
  99 + @Override
  100 + protected void processDelete(Delete delete, int index, String sql, Object obj) {
  101 + if (ignoreTable(delete.getTable().getName())) {
  102 + // 过滤退出执行
  103 + return;
  104 + }
  105 + delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
  106 + }
  107 +
  108 + /**
  109 + * delete update 语句 where 处理
  110 + */
  111 + protected BinaryExpression andExpression(Table table, Expression where) {
  112 + //获得where条件表达式
  113 + EqualsTo equalsTo = new EqualsTo();
  114 + equalsTo.setLeftExpression(this.getAliasColumn(table));
  115 + equalsTo.setRightExpression(getTenantId());
  116 + if (null != where) {
  117 + if (where instanceof OrExpression) {
  118 + return new AndExpression(equalsTo, new Parenthesis(where));
  119 + } else {
  120 + return new AndExpression(equalsTo, where);
  121 + }
  122 + }
  123 + return equalsTo;
  124 + }
  125 +
  126 + /**
  127 + * 追加 SelectItem
  128 + *
  129 + * @param selectItems SelectItem
  130 + */
  131 + protected void appendSelectItem(List<SelectItem> selectItems) {
  132 + if (CollectionUtils.isEmpty(selectItems)) return;
  133 + if (selectItems.size() == 1) {
  134 + SelectItem item = selectItems.get(0);
  135 + if (item instanceof AllColumns || item instanceof AllTableColumns) return;
  136 + }
  137 + selectItems.add(new SelectExpressionItem(new Column(getTenantIdColumn())));
  138 + }
  139 +
  140 + /**
  141 + * 处理 PlainSelect
  142 + */
  143 + protected void processPlainSelect(PlainSelect plainSelect) {
  144 + FromItem fromItem = plainSelect.getFromItem();
  145 + Expression where = plainSelect.getWhere();
  146 + processWhereSubSelect(where);
  147 + if (fromItem instanceof Table) {
  148 + Table fromTable = (Table) fromItem;
  149 + if (!ignoreTable(fromTable.getName())) {
  150 + //#1186 github
  151 + plainSelect.setWhere(builderExpression(where, fromTable));
  152 + }
  153 + } else {
  154 + processFromItem(fromItem);
  155 + }
  156 + //#3087 github
  157 + List<SelectItem> selectItems = plainSelect.getSelectItems();
  158 + if (CollectionUtils.isNotEmpty(selectItems)) {
  159 + selectItems.forEach(this::processSelectItem);
  160 + }
  161 + List<Join> joins = plainSelect.getJoins();
  162 + if (CollectionUtils.isNotEmpty(joins)) {
  163 + processJoins(joins);
  164 + }
  165 + }
  166 +
  167 + /**
  168 + * 处理where条件内的子查询
  169 + * <p>
  170 + * 支持如下:
  171 + * 1. in
  172 + * 2. =
  173 + * 3. >
  174 + * 4. <
  175 + * 5. >=
  176 + * 6. <=
  177 + * 7. <>
  178 + * 8. EXISTS
  179 + * 9. NOT EXISTS
  180 + * <p>
  181 + * 前提条件:
  182 + * 1. 子查询必须放在小括号中
  183 + * 2. 子查询一般放在比较操作符的右边
  184 + *
  185 + * @param where where 条件
  186 + */
  187 + protected void processWhereSubSelect(Expression where) {
  188 + if (where == null) {
  189 + return;
  190 + }
  191 + if (where instanceof FromItem) {
  192 + processFromItem((FromItem) where);
  193 + return;
  194 + }
  195 + if (where.toString().indexOf("SELECT") > 0) {
  196 + // 有子查询
  197 + if (where instanceof BinaryExpression) {
  198 + // 比较符号 , and , or , 等等
  199 + BinaryExpression expression = (BinaryExpression) where;
  200 + processWhereSubSelect(expression.getLeftExpression());
  201 + processWhereSubSelect(expression.getRightExpression());
  202 + } else if (where instanceof InExpression) {
  203 + // in
  204 + InExpression expression = (InExpression) where;
  205 + ItemsList itemsList = expression.getRightItemsList();
  206 + if (itemsList instanceof SubSelect) {
  207 + processSelectBody(((SubSelect) itemsList).getSelectBody());
  208 + }
  209 + } else if (where instanceof ExistsExpression) {
  210 + // exists
  211 + ExistsExpression expression = (ExistsExpression) where;
  212 + processWhereSubSelect(expression.getRightExpression());
  213 + } else if (where instanceof NotExpression) {
  214 + // not exists
  215 + NotExpression expression = (NotExpression) where;
  216 + processWhereSubSelect(expression.getExpression());
  217 + } else if (where instanceof Parenthesis) {
  218 + Parenthesis expression = (Parenthesis) where;
  219 + processWhereSubSelect(expression.getExpression());
  220 + }
  221 + }
  222 + }
  223 +
  224 + protected void processSelectItem(SelectItem selectItem) {
  225 + if (selectItem instanceof SelectExpressionItem) {
  226 + SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
  227 + if (selectExpressionItem.getExpression() instanceof SubSelect) {
  228 + processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
  229 + } else if (selectExpressionItem.getExpression() instanceof Function) {
  230 + processFunction((Function) selectExpressionItem.getExpression());
  231 + }
  232 + }
  233 + }
  234 +
  235 + /**
  236 + * 处理函数
  237 + * <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
  238 + * <p> fixed gitee pulls/141</p>
  239 + *
  240 + * @param function
  241 + */
  242 + protected void processFunction(Function function) {
  243 + ExpressionList parameters = function.getParameters();
  244 + if (parameters != null) {
  245 + parameters.getExpressions().forEach(expression -> {
  246 + if (expression instanceof SubSelect) {
  247 + processSelectBody(((SubSelect) expression).getSelectBody());
  248 + } else if (expression instanceof Function) {
  249 + processFunction((Function) expression);
  250 + }
  251 + });
  252 + }
  253 + }
  254 +
  255 + /**
  256 + * 处理子查询等
  257 + */
  258 + protected void processFromItem(FromItem fromItem) {
  259 + if (fromItem instanceof SubJoin) {
  260 + SubJoin subJoin = (SubJoin) fromItem;
  261 + if (subJoin.getJoinList() != null) {
  262 + processJoins(subJoin.getJoinList());
  263 + }
  264 + if (subJoin.getLeft() != null) {
  265 + processFromItem(subJoin.getLeft());
  266 + }
  267 + } else if (fromItem instanceof SubSelect) {
  268 + SubSelect subSelect = (SubSelect) fromItem;
  269 + if (subSelect.getSelectBody() != null) {
  270 + processSelectBody(subSelect.getSelectBody());
  271 + }
  272 + } else if (fromItem instanceof ValuesList) {
  273 + logger.debug("Perform a subquery, if you do not give us feedback");
  274 + } else if (fromItem instanceof LateralSubSelect) {
  275 + LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
  276 + if (lateralSubSelect.getSubSelect() != null) {
  277 + SubSelect subSelect = lateralSubSelect.getSubSelect();
  278 + if (subSelect.getSelectBody() != null) {
  279 + processSelectBody(subSelect.getSelectBody());
  280 + }
  281 + }
  282 + }
  283 + }
  284 +
  285 + /**
  286 + * 处理 joins
  287 + *
  288 + * @param joins join 集合
  289 + */
  290 + private void processJoins(List<Join> joins) {
  291 + //对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名
  292 + Deque<Table> tables = new LinkedList<>();
  293 + for (Join join : joins) {
  294 + // 处理 on 表达式
  295 + FromItem fromItem = join.getRightItem();
  296 + if (fromItem instanceof Table) {
  297 + Table fromTable = (Table) fromItem;
  298 + // 获取 join 尾缀的 on 表达式列表
  299 + Collection<Expression> originOnExpressions = join.getOnExpressions();
  300 + // 正常 join on 表达式只有一个,立刻处理
  301 + if (originOnExpressions.size() == 1) {
  302 + processJoin(join);
  303 + continue;
  304 + }
  305 + // 当前表是否忽略
  306 + boolean needIgnore = ignoreTable(fromTable.getName());
  307 + // 表名压栈,忽略的表压入 null,以便后续不处理
  308 + tables.push(needIgnore ? null : fromTable);
  309 + // 尾缀多个 on 表达式的时候统一处理
  310 + if (originOnExpressions.size() > 1) {
  311 + Collection<Expression> onExpressions = new LinkedList<>();
  312 + for (Expression originOnExpression : originOnExpressions) {
  313 + Table currentTable = tables.poll();
  314 + if (currentTable == null) {
  315 + onExpressions.add(originOnExpression);
  316 + } else {
  317 + onExpressions.add(builderExpression(originOnExpression, currentTable));
  318 + }
  319 + }
  320 + join.setOnExpressions(onExpressions);
  321 + }
  322 + } else {
  323 + // 处理右边连接的子表达式
  324 + processFromItem(fromItem);
  325 + }
  326 + }
  327 + }
  328 +
  329 + /**
  330 + * 处理联接语句
  331 + */
  332 + protected void processJoin(Join join) {
  333 + if (join.getRightItem() instanceof Table) {
  334 + Table fromTable = (Table) join.getRightItem();
  335 + if (ignoreTable(fromTable.getName())) {
  336 + // 过滤退出执行
  337 + return;
  338 + }
  339 + // 走到这里说明 on 表达式肯定只有一个
  340 + Collection<Expression> originOnExpressions = join.getOnExpressions();
  341 + List<Expression> onExpressions = new LinkedList<>();
  342 + onExpressions.add(builderExpression(originOnExpressions.iterator().next(), fromTable));
  343 + join.setOnExpressions(onExpressions);
  344 + }
  345 + }
  346 +
  347 + /**
  348 + * 处理条件
  349 + */
  350 + protected Expression builderExpression(Expression currentExpression, Table table) {
  351 + EqualsTo equalsTo = new EqualsTo();
  352 + equalsTo.setLeftExpression(this.getAliasColumn(table));
  353 + equalsTo.setRightExpression(getTenantId());
  354 + if (currentExpression == null) {
  355 + return equalsTo;
  356 + }
  357 + if (currentExpression instanceof OrExpression) {
  358 + return new AndExpression(new Parenthesis(currentExpression), equalsTo);
  359 + } else {
  360 + return new AndExpression(currentExpression, equalsTo);
  361 + }
  362 + }
  363 +
  364 + /**
  365 + * 租户字段别名设置
  366 + * <p>tenantId 或 tableAlias.tenantId</p>
  367 + *
  368 + * @param table 表对象
  369 + * @return 字段
  370 + */
  371 + protected Column getAliasColumn(Table table) {
  372 + StringBuilder column = new StringBuilder();
  373 + if (table.getAlias() != null) {
  374 + column.append(table.getAlias().getName()).append(StringPool.DOT);
  375 + }
  376 + column.append(getTenantIdColumn());
  377 + return new Column(column.toString());
  378 + }
  379 +
  380 +// @Override
  381 +// public void setProperties(Properties properties) {
  382 +// PropertyMapper.newInstance(properties).whenNotBlank("tenantLineHandler",
  383 +// ClassUtils::newInstance, this::setTenantLineHandler);
  384 +// }
  385 +
  386 + // TODO 芋艿:未实现
  387 +
  388 + private boolean ignoreTable(String tableName) {
  389 + return false;
  390 + }
  391 +
  392 + private String getTenantIdColumn() {
  393 + return "dept_id";
  394 + }
  395 +
  396 + private Expression getTenantId() {
  397 + return new LongValue(1L);
  398 + }
  399 +
  400 +}
... ...
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datascope/core/package-info.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.datascope.core;
... ...
yudao-framework/yudao-spring-boot-starter-datascope/src/main/java/cn/iocoder/yudao/framework/datascope/package-info.java renamed to yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datascope/package-info.java
yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datascope/core/interceptor/DataPermissionInterceptorTest.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.datascope.core.interceptor;
  2 +
  3 +import org.junit.jupiter.api.Test;
  4 +
  5 +import static org.assertj.core.api.Assertions.assertThat;
  6 +
  7 +public class DataPermissionInterceptorTest {
  8 +
  9 + private final DataPermissionInterceptor interceptor = new DataPermissionInterceptor();
  10 +
  11 + @Test
  12 + void selectSingle() {
  13 + // 单表
  14 + assertSql("select * from entity where id = ?",
  15 + "SELECT * FROM entity WHERE id = ? AND tenant_id = 1");
  16 +
  17 + assertSql("select * from entity where id = ? or name = ?",
  18 + "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
  19 +
  20 + assertSql("SELECT * FROM entity WHERE (id = ? OR name = ?)",
  21 + "SELECT * FROM entity WHERE (id = ? OR name = ?) AND tenant_id = 1");
  22 +
  23 + /* not */
  24 + assertSql("SELECT * FROM entity WHERE not (id = ? OR name = ?)",
  25 + "SELECT * FROM entity WHERE NOT (id = ? OR name = ?) AND tenant_id = 1");
  26 + }
  27 +
  28 + private void assertSql(String sql, String targetSql) {
  29 + assertThat(interceptor.parserSingle(sql, null)).isEqualTo(targetSql);
  30 + }
  31 +
  32 +}
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -33,7 +33,7 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; {
33 33 }
34 34  
35 35 default Integer selectCount(String field, Object value) {
36   - return selectCount(new QueryWrapper<T>().eq(field, value));
  36 + return selectCount(new QueryWrapper<T>().eq(field, value)).intValue();
37 37 }
38 38  
39 39 default List<T> selectList() {
... ...