✨ 数据权限初稿,能用就行
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>ballcat-starters</artifactId>
|
||||
<groupId>com.hccake</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ballcat-spring-boot-starter-datascope</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
<version>3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- slf4j日志 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
<version>3.5.5</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.hccake.ballcat.common.datascope;
|
||||
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
public interface DataScope {
|
||||
|
||||
/**
|
||||
* 数据所对应的资源
|
||||
* @return 资源标识
|
||||
*/
|
||||
String getResource();
|
||||
|
||||
/**
|
||||
* 该资源相关的所有表,推荐使用 Set 类型。 <br/>
|
||||
* 如需忽略表名大小写判断,则可以使用 TreeSet,并设置忽略大小写的自定义Comparator。 <br/>
|
||||
* eg. new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
* @return tableNames
|
||||
*/
|
||||
Collection<String> getTableNames();
|
||||
|
||||
/**
|
||||
* 根据表名和表别名,动态生成的 where/or 筛选条件
|
||||
* @param tableName 表名
|
||||
* @param tableAlias 表别名,可能为空
|
||||
* @return 数据规则表达式
|
||||
*/
|
||||
Expression getExpression(String tableName, Alias tableAlias);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hccake.ballcat.common.datascope;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* DataScope 持有类 内部维护一个 ThreadLocal,用于存储当前用户的所有 DataScope
|
||||
*
|
||||
* @author Hccake 2020/9/27
|
||||
* @version 1.0
|
||||
*/
|
||||
public class DataScopeHolder {
|
||||
|
||||
private DataScopeHolder() {
|
||||
}
|
||||
|
||||
private static final ThreadLocal<Map<String, DataScope>> DATA_SCOPE_LOCAL = new InheritableThreadLocal<>();
|
||||
|
||||
public static Map<String, DataScope> getDataScopes() {
|
||||
return DataScopeHolder.DATA_SCOPE_LOCAL.get();
|
||||
}
|
||||
|
||||
public static void setDataScopes(Map<String, DataScope> dataScopes) {
|
||||
DataScopeHolder.DATA_SCOPE_LOCAL.set(dataScopes);
|
||||
}
|
||||
|
||||
public static DataScope putDataScope(String key, DataScope dataScope) {
|
||||
if (DataScopeHolder.getDataScopes() == null) {
|
||||
setDataScopes(new HashMap<>(8));
|
||||
}
|
||||
return DataScopeHolder.getDataScopes().put(key, dataScope);
|
||||
}
|
||||
|
||||
public static DataScope removeDataScope(String key) {
|
||||
return DataScopeHolder.getDataScopes().remove(key);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
DATA_SCOPE_LOCAL.remove();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.hccake.ballcat.common.datascope.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限注解,注解在 Mapper类 或者 对应方法上 用于提供该 mapper 对应表,所需控制的实体信息
|
||||
* @author Hccake 2020/9/27
|
||||
* @version 1.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DataPermission {
|
||||
|
||||
/**
|
||||
* 资源类型
|
||||
* @return 资源类型数组
|
||||
*/
|
||||
String[] resources();
|
||||
|
||||
/**
|
||||
* 用于在全局开启或者关闭数据权限时,对指定类或者指定方法进行开关控制
|
||||
* @return boolean 默认返回 true
|
||||
*/
|
||||
boolean enabled() default true;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.hccake.ballcat.common.datascope.handler;
|
||||
|
||||
import com.hccake.ballcat.common.datascope.DataScope;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class DataPermissionHandler {
|
||||
|
||||
List<DataScope> dataScopes = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.hccake.ballcat.common.datascope.interceptor;
|
||||
|
||||
import com.hccake.ballcat.common.datascope.processor.DataScopeSqlProcessor;
|
||||
import com.hccake.ballcat.common.datascope.util.PluginUtils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.SqlCommandType;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 数据权限拦截器
|
||||
*
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Intercepts({
|
||||
@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) })
|
||||
public class DataPermissionInterceptor implements Interceptor {
|
||||
|
||||
private final DataScopeSqlProcessor dataScopeSqlProcessor;
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
// 第一版,测试用
|
||||
Object target = invocation.getTarget();
|
||||
StatementHandler sh = (StatementHandler) target;
|
||||
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
|
||||
MappedStatement ms = mpSh.mappedStatement();
|
||||
SqlCommandType sct = ms.getSqlCommandType();
|
||||
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
|
||||
|
||||
// TODO 根据用户权限判断是否需要拦截,例如管理员可以查看所有,则直接放行
|
||||
// TODO 动态生成 DataPermissionHandler, 根据注解进行一些此次 sql 执行中需要忽略的点
|
||||
if (sct == SqlCommandType.SELECT) {
|
||||
mpBs.sql(dataScopeSqlProcessor.parserSingle(mpBs.sql(), null));
|
||||
}
|
||||
else if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
|
||||
mpBs.sql(dataScopeSqlProcessor.parserMulti(mpBs.sql(), null));
|
||||
}
|
||||
return invocation.proceed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
if (target instanceof StatementHandler) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProperties(Properties properties) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.hccake.ballcat.common.datascope.parser;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.jsqlparser.JSQLParserException;
|
||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||
import net.sf.jsqlparser.statement.Statement;
|
||||
import net.sf.jsqlparser.statement.Statements;
|
||||
import net.sf.jsqlparser.statement.delete.Delete;
|
||||
import net.sf.jsqlparser.statement.insert.Insert;
|
||||
import net.sf.jsqlparser.statement.select.Select;
|
||||
import net.sf.jsqlparser.statement.update.Update;
|
||||
|
||||
/**
|
||||
* https://github.com/JSQLParser/JSqlParser
|
||||
*
|
||||
* @author miemie hccake
|
||||
* @since 2020-06-22
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class JsqlParserSupport {
|
||||
|
||||
public String parserSingle(String sql, Object obj) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("original SQL: " + sql);
|
||||
}
|
||||
try {
|
||||
Statement statement = CCJSqlParserUtil.parse(sql);
|
||||
return processParser(statement, 0, sql, obj);
|
||||
}
|
||||
catch (JSQLParserException e) {
|
||||
throw new RuntimeException(String.format("Failed to process, Error SQL: %s", sql), e);
|
||||
}
|
||||
}
|
||||
|
||||
public String parserMulti(String sql, Object obj) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("original SQL: " + sql);
|
||||
}
|
||||
try {
|
||||
// fixed github pull/295
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Statements statements = CCJSqlParserUtil.parseStatements(sql);
|
||||
int i = 0;
|
||||
for (Statement statement : statements.getStatements()) {
|
||||
if (i > 0) {
|
||||
sb.append(";");
|
||||
}
|
||||
sb.append(processParser(statement, i, sql, obj));
|
||||
i++;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
catch (JSQLParserException e) {
|
||||
throw new RuntimeException(String.format("Failed to process, Error SQL: %s", sql), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 SQL 解析
|
||||
* @param statement JsqlParser Statement
|
||||
* @return sql
|
||||
*/
|
||||
protected String processParser(Statement statement, int index, String sql, Object obj) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("SQL to parse, SQL: " + sql);
|
||||
}
|
||||
if (statement instanceof Insert) {
|
||||
this.processInsert((Insert) statement, index, sql, obj);
|
||||
}
|
||||
else if (statement instanceof Select) {
|
||||
this.processSelect((Select) statement, index, sql, obj);
|
||||
}
|
||||
else if (statement instanceof Update) {
|
||||
this.processUpdate((Update) statement, index, sql, obj);
|
||||
}
|
||||
else if (statement instanceof Delete) {
|
||||
this.processDelete((Delete) statement, index, sql, obj);
|
||||
}
|
||||
sql = statement.toString();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("parse the finished SQL: " + sql);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
protected void processInsert(Insert insert, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
protected void processDelete(Delete delete, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新
|
||||
*/
|
||||
protected void processUpdate(Update update, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
protected void processSelect(Select select, int index, String sql, Object obj) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
package com.hccake.ballcat.common.datascope.processor;
|
||||
|
||||
import com.hccake.ballcat.common.datascope.handler.DataPermissionHandler;
|
||||
import com.hccake.ballcat.common.datascope.parser.JsqlParserSupport;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.jsqlparser.expression.BinaryExpression;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.NotExpression;
|
||||
import net.sf.jsqlparser.expression.Parenthesis;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
||||
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExistsExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import net.sf.jsqlparser.schema.Table;
|
||||
import net.sf.jsqlparser.statement.delete.Delete;
|
||||
import net.sf.jsqlparser.statement.insert.Insert;
|
||||
import net.sf.jsqlparser.statement.select.*;
|
||||
import net.sf.jsqlparser.statement.update.Update;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 数据权限 sql 处理器 参考 mybatis-plus 租户拦截器,解析 sql where 部分,进行查询表达式注入
|
||||
*
|
||||
* @author Hccake 2020/9/26
|
||||
* @version 1.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DataScopeSqlProcessor extends JsqlParserSupport {
|
||||
|
||||
private final DataPermissionHandler dataPermissionHandler;
|
||||
|
||||
/**
|
||||
* select 类型SQL处理
|
||||
* @param select jsqlparser Statement Select
|
||||
*/
|
||||
@Override
|
||||
protected void processSelect(Select select, int index, String sql, Object obj) {
|
||||
processSelectBody(select.getSelectBody());
|
||||
List<WithItem> withItemsList = select.getWithItemsList();
|
||||
if (withItemsList != null && withItemsList.size() != 0) {
|
||||
withItemsList.forEach(this::processSelectBody);
|
||||
}
|
||||
}
|
||||
|
||||
protected void processSelectBody(SelectBody selectBody) {
|
||||
if (selectBody == null) {
|
||||
return;
|
||||
}
|
||||
if (selectBody instanceof PlainSelect) {
|
||||
processPlainSelect((PlainSelect) selectBody);
|
||||
}
|
||||
else if (selectBody instanceof WithItem) {
|
||||
WithItem withItem = (WithItem) selectBody;
|
||||
processSelectBody(withItem.getSelectBody());
|
||||
}
|
||||
else {
|
||||
SetOperationList operationList = (SetOperationList) selectBody;
|
||||
if (operationList.getSelects() != null && operationList.getSelects().size() > 0) {
|
||||
operationList.getSelects().forEach(this::processSelectBody);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* insert 类型SQL处理
|
||||
* @param insert jsqlparser Statement Insert
|
||||
*/
|
||||
@Override
|
||||
protected void processInsert(Insert insert, int index, String sql, Object obj) {
|
||||
// insert 暂时不处理
|
||||
}
|
||||
|
||||
/**
|
||||
* update 类型SQL处理
|
||||
* @param update jsqlparser Statement Update
|
||||
*/
|
||||
@Override
|
||||
protected void processUpdate(Update update, int index, String sql, Object obj) {
|
||||
update.setWhere(this.injectExpression(update.getWhere(), update.getTable()));
|
||||
}
|
||||
|
||||
/**
|
||||
* delete 类型SQL处理
|
||||
* @param delete jsqlparser Statement Delete
|
||||
*/
|
||||
@Override
|
||||
protected void processDelete(Delete delete, int index, String sql, Object obj) {
|
||||
delete.setWhere(this.injectExpression(delete.getWhere(), delete.getTable()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 PlainSelect
|
||||
*/
|
||||
protected void processPlainSelect(PlainSelect plainSelect) {
|
||||
FromItem fromItem = plainSelect.getFromItem();
|
||||
Expression where = plainSelect.getWhere();
|
||||
processWhereSubSelect(where);
|
||||
if (fromItem instanceof Table) {
|
||||
Table fromTable = (Table) fromItem;
|
||||
// #1186 github
|
||||
plainSelect.setWhere(injectExpression(plainSelect.getWhere(), fromTable));
|
||||
}
|
||||
else {
|
||||
processFromItem(fromItem);
|
||||
}
|
||||
List<Join> joins = plainSelect.getJoins();
|
||||
if (joins != null && joins.size() > 0) {
|
||||
joins.forEach(j -> {
|
||||
processJoin(j);
|
||||
processFromItem(j.getRightItem());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理where条件内的子查询
|
||||
* <p>
|
||||
* 支持如下: 1. in 2. = 3. > 4. < 5. >= 6. <= 7. <> 8. EXISTS 9. NOT EXISTS
|
||||
* <p>
|
||||
* 前提条件: 1. 子查询必须放在小括号中 2. 子查询一般放在比较操作符的右边
|
||||
* @param where where 条件
|
||||
*/
|
||||
protected void processWhereSubSelect(Expression where) {
|
||||
if (where == null) {
|
||||
return;
|
||||
}
|
||||
if (where instanceof FromItem) {
|
||||
processFromItem((FromItem) where);
|
||||
return;
|
||||
}
|
||||
if (where.toString().indexOf("SELECT") > 0) {
|
||||
// 有子查询
|
||||
if (where instanceof BinaryExpression) {
|
||||
// 比较符号 , and , or , 等等
|
||||
BinaryExpression expression = (BinaryExpression) where;
|
||||
processWhereSubSelect(expression.getLeftExpression());
|
||||
processWhereSubSelect(expression.getRightExpression());
|
||||
}
|
||||
else if (where instanceof InExpression) {
|
||||
// in
|
||||
InExpression expression = (InExpression) where;
|
||||
ItemsList itemsList = expression.getRightItemsList();
|
||||
if (itemsList instanceof SubSelect) {
|
||||
processSelectBody(((SubSelect) itemsList).getSelectBody());
|
||||
}
|
||||
}
|
||||
else if (where instanceof ExistsExpression) {
|
||||
// exists
|
||||
ExistsExpression expression = (ExistsExpression) where;
|
||||
processWhereSubSelect(expression.getRightExpression());
|
||||
}
|
||||
else if (where instanceof NotExpression) {
|
||||
// not exists
|
||||
NotExpression expression = (NotExpression) where;
|
||||
processWhereSubSelect(expression.getExpression());
|
||||
}
|
||||
else if (where instanceof Parenthesis) {
|
||||
Parenthesis expression = (Parenthesis) where;
|
||||
processWhereSubSelect(expression.getExpression());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理子查询等
|
||||
*/
|
||||
protected void processFromItem(FromItem fromItem) {
|
||||
if (fromItem instanceof SubJoin) {
|
||||
SubJoin subJoin = (SubJoin) fromItem;
|
||||
if (subJoin.getJoinList() != null) {
|
||||
subJoin.getJoinList().forEach(this::processJoin);
|
||||
}
|
||||
if (subJoin.getLeft() != null) {
|
||||
processFromItem(subJoin.getLeft());
|
||||
}
|
||||
}
|
||||
else if (fromItem instanceof SubSelect) {
|
||||
SubSelect subSelect = (SubSelect) fromItem;
|
||||
if (subSelect.getSelectBody() != null) {
|
||||
processSelectBody(subSelect.getSelectBody());
|
||||
}
|
||||
}
|
||||
else if (fromItem instanceof ValuesList) {
|
||||
log.debug("Perform a subquery, if you do not give us feedback");
|
||||
}
|
||||
else if (fromItem instanceof LateralSubSelect) {
|
||||
LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
|
||||
if (lateralSubSelect.getSubSelect() != null) {
|
||||
SubSelect subSelect = lateralSubSelect.getSubSelect();
|
||||
if (subSelect.getSelectBody() != null) {
|
||||
processSelectBody(subSelect.getSelectBody());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理联接语句
|
||||
*/
|
||||
protected void processJoin(Join join) {
|
||||
if (join.getRightItem() instanceof Table) {
|
||||
Table fromTable = (Table) join.getRightItem();
|
||||
join.setOnExpression(injectExpression(join.getOnExpression(), fromTable));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 DataScope ,将数据过滤的表达式注入原本的 where/or 条件
|
||||
* @param currentExpression Expression where/or
|
||||
* @param table 表信息
|
||||
* @return 修改后的 where/or 条件
|
||||
*/
|
||||
private Expression injectExpression(Expression currentExpression, Table table) {
|
||||
// TODO 重写 dataPermissionHandler
|
||||
// TODO 当用户检索到 dataScope 所属字段时,需要判断该值是否在 scope 中,然后将其合并
|
||||
String tableName = table.getName();
|
||||
Expression dataFilterExpression = dataPermissionHandler.getDataScopes().stream()
|
||||
.filter(x -> x.getTableNames().contains(tableName))
|
||||
.map(x -> x.getExpression(tableName, table.getAlias())).filter(Objects::nonNull)
|
||||
.reduce(AndExpression::new).orElse(null);
|
||||
|
||||
if (currentExpression == null) {
|
||||
return dataFilterExpression;
|
||||
}
|
||||
if (dataFilterExpression == null) {
|
||||
return currentExpression;
|
||||
}
|
||||
if (currentExpression instanceof OrExpression) {
|
||||
return new AndExpression(new Parenthesis(currentExpression), dataFilterExpression);
|
||||
}
|
||||
else {
|
||||
return new AndExpression(currentExpression, dataFilterExpression);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前表是否有别名,动态对字段名前添加表别名 eg. 表名: table_1 as t 原始字段:column1 返回: t.column1
|
||||
* @param table 表信息
|
||||
* @param column 字段名
|
||||
* @return 原始字段名,或者添加了表别名的字段名
|
||||
*/
|
||||
protected Column getAliasColumn(Table table, String column) {
|
||||
StringBuilder columnBuilder = new StringBuilder();
|
||||
if (table.getAlias() != null) {
|
||||
columnBuilder.append(table.getAlias().getName()).append(".");
|
||||
}
|
||||
columnBuilder.append(column);
|
||||
return new Column(columnBuilder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2020, baomidou (jobob@qq.com).
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
* <p>
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package com.hccake.ballcat.common.datascope.util;
|
||||
|
||||
import org.apache.ibatis.executor.Executor;
|
||||
import org.apache.ibatis.executor.parameter.ParameterHandler;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.BoundSql;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.mapping.ParameterMapping;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.apache.ibatis.session.Configuration;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 插件工具类
|
||||
*
|
||||
* @author TaoYu , hubin
|
||||
* @since 2017-06-20
|
||||
*/
|
||||
public abstract class PluginUtils {
|
||||
|
||||
public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";
|
||||
|
||||
/**
|
||||
* 获得真正的处理对象,可能多层代理.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T realTarget(Object target) {
|
||||
if (Proxy.isProxyClass(target.getClass())) {
|
||||
MetaObject metaObject = SystemMetaObject.forObject(target);
|
||||
return realTarget(metaObject.getValue("h.target"));
|
||||
}
|
||||
return (T) target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给 BoundSql 设置 additionalParameters
|
||||
* @param boundSql BoundSql
|
||||
* @param additionalParameters additionalParameters
|
||||
*/
|
||||
public static void setAdditionalParameter(BoundSql boundSql, Map<String, Object> additionalParameters) {
|
||||
additionalParameters.forEach(boundSql::setAdditionalParameter);
|
||||
}
|
||||
|
||||
public static MPBoundSql mpBoundSql(BoundSql boundSql) {
|
||||
return new MPBoundSql(boundSql);
|
||||
}
|
||||
|
||||
public static MPStatementHandler mpStatementHandler(StatementHandler statementHandler) {
|
||||
statementHandler = realTarget(statementHandler);
|
||||
MetaObject object = SystemMetaObject.forObject(statementHandler);
|
||||
return new MPStatementHandler(SystemMetaObject.forObject(object.getValue("delegate")));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link org.apache.ibatis.executor.statement.BaseStatementHandler}
|
||||
*/
|
||||
public static class MPStatementHandler {
|
||||
|
||||
private final MetaObject statementHandler;
|
||||
|
||||
MPStatementHandler(MetaObject statementHandler) {
|
||||
this.statementHandler = statementHandler;
|
||||
}
|
||||
|
||||
public ParameterHandler parameterHandler() {
|
||||
return get("parameterHandler");
|
||||
}
|
||||
|
||||
public MappedStatement mappedStatement() {
|
||||
return get("mappedStatement");
|
||||
}
|
||||
|
||||
public Executor executor() {
|
||||
return get("executor");
|
||||
}
|
||||
|
||||
public MPBoundSql mPBoundSql() {
|
||||
return new MPBoundSql(boundSql());
|
||||
}
|
||||
|
||||
public BoundSql boundSql() {
|
||||
return get("boundSql");
|
||||
}
|
||||
|
||||
public Configuration configuration() {
|
||||
return get("configuration");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T get(String property) {
|
||||
return (T) statementHandler.getValue(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BoundSql}
|
||||
*/
|
||||
public static class MPBoundSql {
|
||||
|
||||
private final MetaObject boundSql;
|
||||
|
||||
private final BoundSql delegate;
|
||||
|
||||
MPBoundSql(BoundSql boundSql) {
|
||||
this.delegate = boundSql;
|
||||
this.boundSql = SystemMetaObject.forObject(boundSql);
|
||||
}
|
||||
|
||||
public String sql() {
|
||||
return delegate.getSql();
|
||||
}
|
||||
|
||||
public void sql(String sql) {
|
||||
boundSql.setValue("sql", sql);
|
||||
}
|
||||
|
||||
public List<ParameterMapping> parameterMappings() {
|
||||
List<ParameterMapping> parameterMappings = delegate.getParameterMappings();
|
||||
return new ArrayList<>(parameterMappings);
|
||||
}
|
||||
|
||||
public void parameterMappings(List<ParameterMapping> parameterMappings) {
|
||||
boundSql.setValue("parameterMappings", Collections.unmodifiableList(parameterMappings));
|
||||
}
|
||||
|
||||
public Object parameterObject() {
|
||||
return get("parameterObject");
|
||||
}
|
||||
|
||||
public Map<String, Object> additionalParameters() {
|
||||
return get("additionalParameters");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T get(String property) {
|
||||
return (T) boundSql.getValue(property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.hccake.ballcat.common.datascope.test;
|
||||
|
||||
import com.hccake.ballcat.common.datascope.DataScope;
|
||||
import com.hccake.ballcat.common.datascope.DataScopeHolder;
|
||||
import com.hccake.ballcat.common.datascope.handler.DataPermissionHandler;
|
||||
import com.hccake.ballcat.common.datascope.processor.DataScopeSqlProcessor;
|
||||
import net.sf.jsqlparser.expression.Alias;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
import net.sf.jsqlparser.expression.StringValue;
|
||||
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
|
||||
import net.sf.jsqlparser.expression.operators.relational.InExpression;
|
||||
import net.sf.jsqlparser.schema.Column;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Hccake 2020/9/28
|
||||
* @version 1.0
|
||||
*/
|
||||
public class SqlParseTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
DataScope dataScope = new DataScope() {
|
||||
final String columnId = "order_id";
|
||||
|
||||
@Override
|
||||
public String getResource() {
|
||||
return "order";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getTableNames() {
|
||||
Set<String> tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
tableNames.addAll(Arrays.asList("t_order", "t_order_info"));
|
||||
return tableNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Expression getExpression(String tableName, Alias tableAlias) {
|
||||
Column column = new Column(tableAlias == null ? columnId : tableAlias.getName() + "." + columnId);
|
||||
ExpressionList expressionList = new ExpressionList();
|
||||
expressionList.setExpressions(Arrays.asList(new StringValue("1"), new StringValue("2")));
|
||||
return new InExpression(column, expressionList);
|
||||
}
|
||||
};
|
||||
|
||||
DataPermissionHandler dataPermissionHandler = new DataPermissionHandler();
|
||||
List<DataScope> list = new ArrayList<>();
|
||||
list.add(dataScope);
|
||||
dataPermissionHandler.setDataScopes(list);
|
||||
|
||||
DataScopeSqlProcessor dataScopeSqlProcessor = new DataScopeSqlProcessor(dataPermissionHandler);
|
||||
|
||||
DataScopeHolder.putDataScope("order", dataScope);
|
||||
String sql = "select o.order_id,o.order_name,oi.order_price "
|
||||
+ "from t_ORDER o left join t_order_info oi on o.order_id = oi.order_id "
|
||||
+ "where oi.order_price > 100";
|
||||
|
||||
dataScopeSqlProcessor.parserSingle(sql, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,15 +13,16 @@
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>ballcat-spring-boot-starter-log</module>
|
||||
<module>ballcat-spring-boot-starter-job</module>
|
||||
<module>ballcat-spring-boot-starter-swagger</module>
|
||||
<module>ballcat-spring-boot-starter-storage</module>
|
||||
<module>ballcat-spring-boot-starter-mail</module>
|
||||
<module>ballcat-spring-boot-starter-easyexcel</module>
|
||||
<module>ballcat-spring-boot-starter-redis</module>
|
||||
<module>ballcat-spring-boot-starter-datascope</module>
|
||||
<module>ballcat-spring-boot-starter-dingtalk</module>
|
||||
<module>ballcat-spring-boot-starter-kafka</module>
|
||||
<module>ballcat-spring-boot-starter-easyexcel</module>
|
||||
<module>ballcat-spring-boot-starter-job</module>
|
||||
<module>ballcat-spring-boot-starter-kafka</module>
|
||||
<module>ballcat-spring-boot-starter-log</module>
|
||||
<module>ballcat-spring-boot-starter-mail</module>
|
||||
<module>ballcat-spring-boot-starter-redis</module>
|
||||
<module>ballcat-spring-boot-starter-storage</module>
|
||||
<module>ballcat-spring-boot-starter-swagger</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
Reference in New Issue
Block a user