Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BaseMultiTableInnerInterceptor 处理多表时,会错误地把where条件附加到left join的on联表条件中 #5186

Open
GeTOUO opened this issue Mar 10, 2023 · 6 comments

Comments

@GeTOUO
Copy link

GeTOUO commented Mar 10, 2023

当前使用版本(必填,否则不予处理)

V3.5.3.1

该问题是如何引起的?(确定最新版也有问题再提!!!)

BaseMultiTableInnerInterceptor 在执行 processPlainSelect(final PlainSelect plainSelect, final String whereSegment) 方法时逻辑有漏洞

重现步骤(如果有就写完整)

  1. 下列sql是一个联表查询用户及其对应的角色已经授予了哪些数据权限范围,通过拦截器,会在执行该sql时,同样进行数据权限的过滤,过滤的逻辑是AUTH_SUBJECT 表的数据内容,但是条件的执行时,过滤的条件语句被错误地添加到了on的联表语句中。如果这是一个 join连接这没有问题,但对于 LEFT JOIN来说会左表的数据会超出范围最终导致结果集返回了过多的数据。
SELECT
	SUB.id AS subject_id,
	SUB.NAME AS subject_name,
	SRREL.ROLE_ID AS role_id,
	SCOPE.ATTR_TYPE AS scope_type,
	SCOPE.ATTR_ID AS scope_id,
	...
FROM
	AUTH_SUBJECT SUB
	LEFT JOIN AUTH_SUBJECT_ROLE_REL SRREL ON SUB.ID = SRREL.SUBJECT_ID
	LEFT JOIN AUTH_SUBJECT_ROLE_REL_SCOPE_CONFIG SCOPE ON SRREL.ID = SCOPE.SUBJECT_ROLE_REL_ID 
WHERE SUB.ID IN (?)
  1. 期望拦截器处理上述查询时,仅对AUTH_SUBJECT表追加where条件,但实际处理中,条件会被追加到on语句中。

报错信息

不会报错,但此处设计是有问题的。

  1. 是否因为在 processPlainSelect 方法中的 处理 join 环境,应该考虑 左连接或右连接特殊处理一下?不能把数据筛选条件和联表条件做and。
@huayanYu
Copy link
Member

PR

@miemieYaho
Copy link
Member

miemieYaho commented Aug 2, 2023

SELECT * FROM entity e 
left join entity1 e1 on e1.id = e.id 
WHERE e.id = ?

变为

SELECT * FROM entity e 
LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 
WHERE (e.id = ?) AND e.tenant_id = 1

不对吗?

@GeTOUO
Copy link
Author

GeTOUO commented Aug 2, 2023

租户和权限过滤的需求还是不一样吧

SELECT * FROM entity e 

left join entity1 e1 on e1.id = e.id 

WHERE e.id = ?

变为

SELECT * FROM entity e 

LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 

WHERE (e.id = ?) AND e.tenant_id = 1

不对吗?

@HillCheuang
Copy link

我也遇到了这个问题 后续是怎么解决的

@HillCheuang
Copy link

目前我的解决方案是如下: 复制把BaseMultiTableInnerInterceptor的大部分方法复制出来(因为processJoins方法是私有化的方法导致不能直接重写) 然后主要是重写了processJoins 具体代码如下:

    private List<Table> processJoins(List<Table> mainTables, List<Join> joins, final String whereSegment) {
        // join 表达式中最终的主表
        Table mainTable = null;
        // 当前 join 的左表
        Table leftTable = null;

        if (mainTables.size() == 1) {
            mainTable = mainTables.get(0);
            leftTable = mainTable;
        }

        //对于 on 表达式写在最后的 join,需要记录下前面多个 on 的表名
        Deque<List<Table>> onTableDeque = new LinkedList<>();
        for (Join join : joins) {
            // 处理 on 表达式
            FromItem joinItem = join.getRightItem();

            // 获取当前 join 的表,subJoint 可以看作是一张表
            List<Table> joinTables = null;
            if (joinItem instanceof Table) {
                joinTables = new ArrayList<>();
                joinTables.add((Table) joinItem);
            } else if (joinItem instanceof SubJoin) {
                joinTables = processSubJoin((SubJoin) joinItem, whereSegment);
            }

            if (joinTables != null) {

                // 如果是隐式内连接
                if (join.isSimple()) {
                    mainTables.addAll(joinTables);
                    continue;
                }

                // 当前表是否忽略
                Table joinTable = joinTables.get(0);

                List<Table> onTables = null;
                // 如果不要忽略,且是右连接,则记录下当前表
                if (join.isRight()) {
                    mainTable = joinTable;
                    mainTables.clear();
                    if (leftTable != null) {
                        onTables = Collections.singletonList(leftTable);
                    }
                } else if (join.isInner()) {
                    if (mainTable == null) {
                        onTables = Collections.singletonList(joinTable);
                    } else {
                        onTables = Arrays.asList(mainTable, joinTable);
                    }
//                    mainTable = null;
//                    mainTables.clear();
                } else  if(join.isLeft()){
                    //左链接
                    onTables = Collections.singletonList(joinTable);
                }else{
                    onTables = Collections.singletonList(joinTable);
                }
                if (CollectionUtils.isNotEmpty(onTables)) {
                    onTables.forEach(table -> {
                        if (table != null && !mainTables.contains(table)) {
                            mainTables.add(table);
                        }
                    });
                }

                if (mainTable != null && !mainTables.contains(mainTable)) {
                    mainTables.add(mainTable);
                }

                // 获取 join 尾缀的 on 表达式列表
                Collection<Expression> originOnExpressions = join.getOnExpressions();
                // 正常 join on 表达式只有一个,立刻处理
                if (originOnExpressions.size() == 1 && onTables != null) {
//                    List<Expression> onExpressions = new LinkedList<>();
//                    onExpressions.add(builderExpression(originOnExpressions.iterator().next(), onTables, whereSegment));
//                    join.setOnExpressions(onExpressions);
                    leftTable = mainTable == null ? joinTable : mainTable;
                    continue;
                }
                // 表名压栈,忽略的表压入 null,以便后续不处理
                onTableDeque.push(onTables);
                // 尾缀多个 on 表达式的时候统一处理
                if (originOnExpressions.size() > 1) {
                    Collection<Expression> onExpressions = new LinkedList<>();
                    for (Expression originOnExpression : originOnExpressions) {
                        List<Table> currentTableList = onTableDeque.poll();
                        if (CollectionUtils.isEmpty(currentTableList)) {
                            onExpressions.add(originOnExpression);
                        } else {
                            onExpressions.add(builderExpression(originOnExpression, currentTableList, whereSegment));
                        }
                    }
                    join.setOnExpressions(onExpressions);
                }
                leftTable = joinTable;
            } else {
                processOtherFromItem(joinItem, whereSegment);
                leftTable = null;
            }
        }

        return mainTables;
    }

这个时候的mainTables就包含所有的 join 的table 和驱动表

 private List<Table> processSubJoin(SubJoin subJoin, final String whereSegment) {
        List<Table> mainTables = new ArrayList<>();
        if (subJoin.getJoinList() != null) {
            List<Table> list = processFromItem(subJoin.getLeft(), whereSegment);
            mainTables.addAll(list);
            mainTables = processJoins(mainTables, subJoin.getJoinList(), whereSegment);
        }
        return mainTables;
    }

不过值得注意的是 我把拼接on条件的数据权限注释掉了

@HillCheuang
Copy link

租户和权限过滤的需求还是不一样吧

SELECT * FROM entity e 

left join entity1 e1 on e1.id = e.id 

WHERE e.id = ?

变为

SELECT * FROM entity e 

LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 

WHERE (e.id = ?) AND e.tenant_id = 1

不对吗?

实际上运行效果是 将where条件拼到了left join 里面 但是没有拼到where条件里面


SELECT * FROM entity e 

LEFT JOIN entity1 e1 ON e1.id = e.id AND e1.tenant_id = 1 

WHERE (e.id = ?) 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants