diff --git a/jeecg-boot/jeecg-boot-base-core/pom.xml b/jeecg-boot/jeecg-boot-base-core/pom.xml index 2c41434bd..ba0bda318 100644 --- a/jeecg-boot/jeecg-boot-base-core/pom.xml +++ b/jeecg-boot/jeecg-boot-base-core/pom.xml @@ -112,7 +112,15 @@ com.baomidou - mybatis-plus-boot-starter + mybatis-plus-spring-boot3-starter + ${mybatis-plus.version} + + + com.baomidou + + mybatis-plus-jsqlparser + + ${mybatis-plus.version} diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java index c9c4c901d..aaf0a61f7 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/aspect/AutoLogAspect.java @@ -20,7 +20,6 @@ import org.jeecg.common.system.vo.LoginUser; import org.jeecg.common.util.IpUtils; import org.jeecg.common.util.SpringContextUtils; import org.jeecg.common.util.oConvertUtils; -import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.StandardReflectionParameterNameDiscoverer; import org.springframework.stereotype.Component; import org.springframework.validation.BindingResult; diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionAstNodeVisitor.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionAstNodeVisitor.java index 12444d9a9..77d21e4a3 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionAstNodeVisitor.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionAstNodeVisitor.java @@ -1,33 +1,33 @@ -package org.jeecg.common.util.sqlInjection; - -import net.sf.jsqlparser.parser.CCJSqlParserDefaultVisitor; -import net.sf.jsqlparser.parser.SimpleNode; -import net.sf.jsqlparser.statement.select.UnionOp; -import org.jeecg.common.exception.JeecgSqlInjectionException; - -/** - * 基于抽象语法树(AST)的注入攻击分析实现 - * - * @author guyadong - */ -public class InjectionAstNodeVisitor extends CCJSqlParserDefaultVisitor { - public InjectionAstNodeVisitor() { - } - - /** - * 处理禁止联合查询 - * - * @param node - * @param data - * @return - */ - @Override - public Object visit(SimpleNode node, Object data) { - Object value = node.jjtGetValue(); - if (value instanceof UnionOp) { - throw new JeecgSqlInjectionException("DISABLE UNION"); - } - return super.visit(node, data); - } -} - +//package org.jeecg.common.util.sqlInjection; +// +//import net.sf.jsqlparser.parser.CCJSqlParserDefaultVisitor; +//import net.sf.jsqlparser.parser.SimpleNode; +//import net.sf.jsqlparser.statement.select.UnionOp; +//import org.jeecg.common.exception.JeecgSqlInjectionException; +// +///** +// * 基于抽象语法树(AST)的注入攻击分析实现 +// * +// * @author guyadong +// */ +//public class InjectionAstNodeVisitor extends CCJSqlParserDefaultVisitor { +// public InjectionAstNodeVisitor() { +// } +// +// /** +// * 处理禁止联合查询 +// * +// * @param node +// * @param data +// * @return +// */ +// @Override +// public Object visit(SimpleNode node, Object data) { +// Object value = node.jjtGetValue(); +// if (value instanceof UnionOp) { +// throw new JeecgSqlInjectionException("DISABLE UNION"); +// } +// return super.visit(node, data); +// } +//} +// diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionSyntaxObjectAnalyzer.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionSyntaxObjectAnalyzer.java index b31f7c362..28a515272 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionSyntaxObjectAnalyzer.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/InjectionSyntaxObjectAnalyzer.java @@ -1,172 +1,172 @@ -package org.jeecg.common.util.sqlInjection; - - -import net.sf.jsqlparser.expression.BinaryExpression; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.Function; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.expression.operators.conditional.OrExpression; -import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator; -import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.statement.select.Join; -import net.sf.jsqlparser.statement.select.OrderByElement; -import net.sf.jsqlparser.statement.select.PlainSelect; -import net.sf.jsqlparser.statement.select.SelectItem; -import net.sf.jsqlparser.statement.select.SubSelect; -import net.sf.jsqlparser.statement.select.WithItem; -import net.sf.jsqlparser.util.TablesNamesFinder; -import org.jeecg.common.exception.JeecgSqlInjectionException; -import org.jeecg.common.util.sqlInjection.parse.ConstAnalyzer; -import org.jeecg.common.util.sqlInjection.parse.ParserSupport; - -/** - * 基于SQL语法对象的SQL注入攻击分析实现 - * - * @author guyadong - */ -public class InjectionSyntaxObjectAnalyzer extends TablesNamesFinder { - /** - * 危险函数名 - */ - private static final String DANGROUS_FUNCTIONS = "(sleep|benchmark|extractvalue|updatexml|ST_LatFromGeoHash|ST_LongFromGeoHash|GTID_SUBSET|GTID_SUBTRACT|floor|ST_Pointfromgeohash" - + "|geometrycollection|multipoint|polygon|multipolygon|linestring|multilinestring)"; - - private static ThreadLocal disableSubselect = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return true; - } - }; - private ConstAnalyzer constAnalyzer = new ConstAnalyzer(); - - public InjectionSyntaxObjectAnalyzer() { - super(); - init(true); - - } - - @Override - public void visitBinaryExpression(BinaryExpression binaryExpression) { - if (binaryExpression instanceof ComparisonOperator) { - if (isConst(binaryExpression.getLeftExpression()) && isConst(binaryExpression.getRightExpression())) { - /** 禁用恒等式 */ - throw new JeecgSqlInjectionException("DISABLE IDENTICAL EQUATION " + binaryExpression); - } - } - super.visitBinaryExpression(binaryExpression); - } - - @Override - public void visit(AndExpression andExpression) { - super.visit(andExpression); - checkConstExpress(andExpression.getLeftExpression()); - checkConstExpress(andExpression.getRightExpression()); - } - - @Override - public void visit(OrExpression orExpression) { - super.visit(orExpression); - checkConstExpress(orExpression.getLeftExpression()); - checkConstExpress(orExpression.getRightExpression()); - } - - @Override - public void visit(Function function) { - if (function.getName().matches(DANGROUS_FUNCTIONS)) { - /** 禁用危险函数 */ - throw new JeecgSqlInjectionException("DANGROUS FUNCTION: " + function.getName()); - } - super.visit(function); - } - - @Override - public void visit(WithItem withItem) { - try { - /** 允许 WITH 语句中的子查询 */ - disableSubselect.set(false); - super.visit(withItem); - } finally { - disableSubselect.set(true); - } - } - - @Override - public void visit(SubSelect subSelect) { - try { - /** 允许语句中的子查询 */ - disableSubselect.set(false); - super.visit(subSelect); - } finally { - disableSubselect.set(true); - } -// if (disableSubselect.get()) { -// // 禁用子查询 -// throw new JeecgSqlInjectionException("DISABLE subselect " + subSelect); +//package org.jeecg.common.util.sqlInjection; +// +// +//import net.sf.jsqlparser.expression.BinaryExpression; +//import net.sf.jsqlparser.expression.Expression; +//import net.sf.jsqlparser.expression.Function; +//import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +//import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +//import net.sf.jsqlparser.expression.operators.relational.ComparisonOperator; +//import net.sf.jsqlparser.schema.Column; +//import net.sf.jsqlparser.statement.select.Join; +//import net.sf.jsqlparser.statement.select.OrderByElement; +//import net.sf.jsqlparser.statement.select.PlainSelect; +//import net.sf.jsqlparser.statement.select.SelectItem; +//import net.sf.jsqlparser.statement.select.SubSelect; +//import net.sf.jsqlparser.statement.select.WithItem; +//import net.sf.jsqlparser.util.TablesNamesFinder; +//import org.jeecg.common.exception.JeecgSqlInjectionException; +//import org.jeecg.common.util.sqlInjection.parse.ConstAnalyzer; +//import org.jeecg.common.util.sqlInjection.parse.ParserSupport; +// +///** +// * 基于SQL语法对象的SQL注入攻击分析实现 +// * +// * @author guyadong +// */ +//public class InjectionSyntaxObjectAnalyzer extends TablesNamesFinder { +// /** +// * 危险函数名 +// */ +// private static final String DANGROUS_FUNCTIONS = "(sleep|benchmark|extractvalue|updatexml|ST_LatFromGeoHash|ST_LongFromGeoHash|GTID_SUBSET|GTID_SUBTRACT|floor|ST_Pointfromgeohash" +// + "|geometrycollection|multipoint|polygon|multipolygon|linestring|multilinestring)"; +// +// private static ThreadLocal disableSubselect = new ThreadLocal() { +// @Override +// protected Boolean initialValue() { +// return true; // } - } - - @Override - public void visit(Column tableColumn) { - if (ParserSupport.isBoolean(tableColumn)) { - throw new JeecgSqlInjectionException("DISABLE CONST BOOL " + tableColumn); - } - super.visit(tableColumn); - } - - @Override - public void visit(PlainSelect plainSelect) { - if (plainSelect.getSelectItems() != null) { - for (SelectItem item : plainSelect.getSelectItems()) { - item.accept(this); - } - } - - if (plainSelect.getFromItem() != null) { - plainSelect.getFromItem().accept(this); - } - - if (plainSelect.getJoins() != null) { - for (Join join : plainSelect.getJoins()) { - join.getRightItem().accept(this); - for (Expression e : join.getOnExpressions()) { - e.accept(this); - } - } - } - if (plainSelect.getWhere() != null) { - plainSelect.getWhere().accept(this); - checkConstExpress(plainSelect.getWhere()); - } - - if (plainSelect.getHaving() != null) { - plainSelect.getHaving().accept(this); - } - - if (plainSelect.getOracleHierarchical() != null) { - plainSelect.getOracleHierarchical().accept(this); - } - if (plainSelect.getOrderByElements() != null) { - for (OrderByElement orderByElement : plainSelect.getOrderByElements()) { - orderByElement.getExpression().accept(this); - } - } - if (plainSelect.getGroupBy() != null) { - for (Expression expression : plainSelect.getGroupBy().getGroupByExpressionList().getExpressions()) { - expression.accept(this); - } - } - } - - private boolean isConst(Expression expression) { - return constAnalyzer.isConstExpression(expression); - } - - private void checkConstExpress(Expression expression) { - if (constAnalyzer.isConstExpression(expression)) { - /** 禁用常量表达式 */ - throw new JeecgSqlInjectionException("DISABLE CONST EXPRESSION " + expression); - } - } -} - - +// }; +// private ConstAnalyzer constAnalyzer = new ConstAnalyzer(); +// +// public InjectionSyntaxObjectAnalyzer() { +// super(); +// init(true); +// +// } +// +// @Override +// public void visitBinaryExpression(BinaryExpression binaryExpression) { +// if (binaryExpression instanceof ComparisonOperator) { +// if (isConst(binaryExpression.getLeftExpression()) && isConst(binaryExpression.getRightExpression())) { +// /** 禁用恒等式 */ +// throw new JeecgSqlInjectionException("DISABLE IDENTICAL EQUATION " + binaryExpression); +// } +// } +// super.visitBinaryExpression(binaryExpression); +// } +// +// @Override +// public void visit(AndExpression andExpression) { +// super.visit(andExpression); +// checkConstExpress(andExpression.getLeftExpression()); +// checkConstExpress(andExpression.getRightExpression()); +// } +// +// @Override +// public void visit(OrExpression orExpression) { +// super.visit(orExpression); +// checkConstExpress(orExpression.getLeftExpression()); +// checkConstExpress(orExpression.getRightExpression()); +// } +// +// @Override +// public void visit(Function function) { +// if (function.getName().matches(DANGROUS_FUNCTIONS)) { +// /** 禁用危险函数 */ +// throw new JeecgSqlInjectionException("DANGROUS FUNCTION: " + function.getName()); +// } +// super.visit(function); +// } +// +// @Override +// public void visit(WithItem withItem) { +// try { +// /** 允许 WITH 语句中的子查询 */ +// disableSubselect.set(false); +// super.visit(withItem); +// } finally { +// disableSubselect.set(true); +// } +// } +// +// @Override +// public void visit(SubSelect subSelect) { +// try { +// /** 允许语句中的子查询 */ +// disableSubselect.set(false); +// super.visit(subSelect); +// } finally { +// disableSubselect.set(true); +// } +//// if (disableSubselect.get()) { +//// // 禁用子查询 +//// throw new JeecgSqlInjectionException("DISABLE subselect " + subSelect); +//// } +// } +// +// @Override +// public void visit(Column tableColumn) { +// if (ParserSupport.isBoolean(tableColumn)) { +// throw new JeecgSqlInjectionException("DISABLE CONST BOOL " + tableColumn); +// } +// super.visit(tableColumn); +// } +// +// @Override +// public void visit(PlainSelect plainSelect) { +// if (plainSelect.getSelectItems() != null) { +// for (SelectItem item : plainSelect.getSelectItems()) { +// item.accept(this); +// } +// } +// +// if (plainSelect.getFromItem() != null) { +// plainSelect.getFromItem().accept(this); +// } +// +// if (plainSelect.getJoins() != null) { +// for (Join join : plainSelect.getJoins()) { +// join.getRightItem().accept(this); +// for (Expression e : join.getOnExpressions()) { +// e.accept(this); +// } +// } +// } +// if (plainSelect.getWhere() != null) { +// plainSelect.getWhere().accept(this); +// checkConstExpress(plainSelect.getWhere()); +// } +// +// if (plainSelect.getHaving() != null) { +// plainSelect.getHaving().accept(this); +// } +// +// if (plainSelect.getOracleHierarchical() != null) { +// plainSelect.getOracleHierarchical().accept(this); +// } +// if (plainSelect.getOrderByElements() != null) { +// for (OrderByElement orderByElement : plainSelect.getOrderByElements()) { +// orderByElement.getExpression().accept(this); +// } +// } +// if (plainSelect.getGroupBy() != null) { +// for (Expression expression : plainSelect.getGroupBy().getGroupByExpressionList().getExpressions()) { +// expression.accept(this); +// } +// } +// } +// +// private boolean isConst(Expression expression) { +// return constAnalyzer.isConstExpression(expression); +// } +// +// private void checkConstExpress(Expression expression) { +// if (constAnalyzer.isConstExpression(expression)) { +// /** 禁用常量表达式 */ +// throw new JeecgSqlInjectionException("DISABLE CONST EXPRESSION " + expression); +// } +// } +//} +// +// diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/SqlInjectionAnalyzer.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/SqlInjectionAnalyzer.java index 61f06a714..88f3fd3e7 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/SqlInjectionAnalyzer.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/SqlInjectionAnalyzer.java @@ -1,65 +1,65 @@ -package org.jeecg.common.util.sqlInjection; - -import org.jeecg.common.exception.JeecgSqlInjectionException; -import org.jeecg.common.util.sqlInjection.parse.ParserSupport; -; - -/** - * SQL注入攻击分析器 - * - * @author guyadong - * 参考: - * https://blog.csdn.net/10km/article/details/127767358 - * https://gitee.com/l0km/sql2java/tree/dev/sql2java-manager/src/main/java/gu/sql2java/parser - */ -public class SqlInjectionAnalyzer { - - //启用/关闭注入攻击检查 - private boolean injectCheckEnable = true; - //防止SQL注入攻击分析实现 - private final InjectionSyntaxObjectAnalyzer injectionChecker; - private final InjectionAstNodeVisitor injectionVisitor; - - public SqlInjectionAnalyzer() { - this.injectionChecker = new InjectionSyntaxObjectAnalyzer(); - this.injectionVisitor = new InjectionAstNodeVisitor(); - } - - /** - * 启用/关闭注入攻击检查,默认启动 - * - * @param enable - * @return - */ - public SqlInjectionAnalyzer injectCheckEnable(boolean enable) { - injectCheckEnable = enable; - return this; - } - - /** - * 对解析后的SQL对象执行注入攻击分析,有注入攻击的危险则抛出异常{@link JeecgSqlInjectionException} - * - * @param sqlParserInfo - * @throws JeecgSqlInjectionException - */ - public ParserSupport.SqlParserInfo injectAnalyse(ParserSupport.SqlParserInfo sqlParserInfo) throws JeecgSqlInjectionException { - if (null != sqlParserInfo && injectCheckEnable) { - /** SQL注入攻击检查 */ - sqlParserInfo.statement.accept(injectionChecker); - sqlParserInfo.simpleNode.jjtAccept(injectionVisitor, null); - } - return sqlParserInfo; - } - - /** - * sql校验 - */ - public static void checkSql(String sql,boolean check){ - SqlInjectionAnalyzer sqlInjectionAnalyzer = new SqlInjectionAnalyzer(); - sqlInjectionAnalyzer.injectCheckEnable(check); - ParserSupport.SqlParserInfo sqlParserInfo = ParserSupport.parse0(sql, null,null); - sqlInjectionAnalyzer.injectAnalyse(sqlParserInfo); - } -} - - +//package org.jeecg.common.util.sqlInjection; +// +//import org.jeecg.common.exception.JeecgSqlInjectionException; +//import org.jeecg.common.util.sqlInjection.parse.ParserSupport; +//; +// +///** +// * SQL注入攻击分析器 +// * +// * @author guyadong +// * 参考: +// * https://blog.csdn.net/10km/article/details/127767358 +// * https://gitee.com/l0km/sql2java/tree/dev/sql2java-manager/src/main/java/gu/sql2java/parser +// */ +//public class SqlInjectionAnalyzer { +// +// //启用/关闭注入攻击检查 +// private boolean injectCheckEnable = true; +// //防止SQL注入攻击分析实现 +// private final InjectionSyntaxObjectAnalyzer injectionChecker; +// private final InjectionAstNodeVisitor injectionVisitor; +// +// public SqlInjectionAnalyzer() { +// this.injectionChecker = new InjectionSyntaxObjectAnalyzer(); +// this.injectionVisitor = new InjectionAstNodeVisitor(); +// } +// +// /** +// * 启用/关闭注入攻击检查,默认启动 +// * +// * @param enable +// * @return +// */ +// public SqlInjectionAnalyzer injectCheckEnable(boolean enable) { +// injectCheckEnable = enable; +// return this; +// } +// +// /** +// * 对解析后的SQL对象执行注入攻击分析,有注入攻击的危险则抛出异常{@link JeecgSqlInjectionException} +// * +// * @param sqlParserInfo +// * @throws JeecgSqlInjectionException +// */ +// public ParserSupport.SqlParserInfo injectAnalyse(ParserSupport.SqlParserInfo sqlParserInfo) throws JeecgSqlInjectionException { +// if (null != sqlParserInfo && injectCheckEnable) { +// /** SQL注入攻击检查 */ +// sqlParserInfo.statement.accept(injectionChecker); +// sqlParserInfo.simpleNode.jjtAccept(injectionVisitor, null); +// } +// return sqlParserInfo; +// } +// +// /** +// * sql校验 +// */ +// public static void checkSql(String sql,boolean check){ +// SqlInjectionAnalyzer sqlInjectionAnalyzer = new SqlInjectionAnalyzer(); +// sqlInjectionAnalyzer.injectCheckEnable(check); +// ParserSupport.SqlParserInfo sqlParserInfo = ParserSupport.parse0(sql, null,null); +// sqlInjectionAnalyzer.injectAnalyse(sqlParserInfo); +// } +//} +// +// diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ConstAnalyzer.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ConstAnalyzer.java index 9b7345c07..492040964 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ConstAnalyzer.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ConstAnalyzer.java @@ -1,569 +1,569 @@ -package org.jeecg.common.util.sqlInjection.parse; - -import net.sf.jsqlparser.expression.*; -import net.sf.jsqlparser.expression.operators.arithmetic.Addition; -import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; -import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift; -import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; -import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift; -import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; -import net.sf.jsqlparser.expression.operators.arithmetic.Concat; -import net.sf.jsqlparser.expression.operators.arithmetic.Division; -import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision; -import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; -import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; -import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.expression.operators.conditional.OrExpression; -import net.sf.jsqlparser.expression.operators.conditional.XorExpression; -import net.sf.jsqlparser.expression.operators.relational.Between; -import net.sf.jsqlparser.expression.operators.relational.EqualsTo; -import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; -import net.sf.jsqlparser.expression.operators.relational.ExpressionList; -import net.sf.jsqlparser.expression.operators.relational.FullTextSearch; -import net.sf.jsqlparser.expression.operators.relational.GeometryDistance; -import net.sf.jsqlparser.expression.operators.relational.GreaterThan; -import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; -import net.sf.jsqlparser.expression.operators.relational.InExpression; -import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; -import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; -import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; -import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; -import net.sf.jsqlparser.expression.operators.relational.JsonOperator; -import net.sf.jsqlparser.expression.operators.relational.LikeExpression; -import net.sf.jsqlparser.expression.operators.relational.Matches; -import net.sf.jsqlparser.expression.operators.relational.MinorThan; -import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; -import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; -import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; -import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; -import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; -import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; -import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; -import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.statement.select.AllColumns; -import net.sf.jsqlparser.statement.select.AllTableColumns; -import net.sf.jsqlparser.statement.select.OrderByElement; -import net.sf.jsqlparser.statement.select.SubSelect; - -/** - * 判断表达是否为常量的分析器 - * - * @author guyadong - */ -public class ConstAnalyzer implements ExpressionVisitor, ItemsListVisitor { - - private static ThreadLocal constFlag = new ThreadLocal() { - @Override - protected Boolean initialValue() { - return true; - } - }; - - @Override - public void visit(NullValue value) { - } - - @Override - public void visit(Function function) { - constFlag.set(false); - } - - @Override - public void visit(SignedExpression expr) { - expr.getExpression().accept(this); - } - - @Override - public void visit(JdbcParameter parameter) { - constFlag.set(false); - } - - @Override - public void visit(JdbcNamedParameter parameter) { - constFlag.set(false); - } - - @Override - public void visit(DoubleValue value) { - - } - - @Override - public void visit(LongValue value) { - - } - - @Override - public void visit(DateValue value) { - - } - - @Override - public void visit(TimeValue value) { - - } - - @Override - public void visit(TimestampValue value) { - - } - - @Override - public void visit(Parenthesis parenthesis) { - parenthesis.getExpression().accept(this); - } - - @Override - public void visit(StringValue value) { - - } - - @Override - public void visit(Addition expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(Division expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(IntegerDivision expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(Multiplication expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(Subtraction expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(AndExpression expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(OrExpression expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(XorExpression expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(Between expr) { - expr.getLeftExpression().accept(this); - expr.getBetweenExpressionStart().accept(this); - expr.getBetweenExpressionEnd().accept(this); - } - - /** - * 用于处理 OverlapsCondition 类型的表达式 - * @param overlapsCondition - */ - @Override - public void visit(OverlapsCondition overlapsCondition) { - constFlag.set(false); - } - /** - * 用于处理 SafeCastExpression 类型的表达式。 - * @param safeCastExpression - */ - @Override - public void visit(SafeCastExpression safeCastExpression) { - constFlag.set(false); - } - - @Override - public void visit(EqualsTo expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(GreaterThan expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(GreaterThanEquals expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(InExpression expr) { - if (expr.getLeftExpression() != null) { - expr.getLeftExpression().accept(this); - } - } - - @Override - public void visit(IsNullExpression expr) { - expr.getLeftExpression().accept(this); - } - - @Override - public void visit(FullTextSearch expr) { - constFlag.set(false); - } - - @Override - public void visit(IsBooleanExpression expr) { - expr.getLeftExpression().accept(this); - } - - @Override - public void visit(LikeExpression expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(MinorThan expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(MinorThanEquals expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(NotEqualsTo expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(Column column) { - if (!ParserSupport.isBoolean(column)) { - constFlag.set(false); - } - } - - @Override - public void visit(SubSelect subSelect) { - constFlag.set(false); - } - - @Override - public void visit(CaseExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(WhenClause expr) { - constFlag.set(false); - } - - @Override - public void visit(ExistsExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(AnyComparisonExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(Concat expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(Matches expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(BitwiseAnd expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(BitwiseOr expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(BitwiseXor expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(CastExpression expr) { - expr.getLeftExpression().accept(this); - } - - @Override - public void visit(TryCastExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(Modulo expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(AnalyticExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(ExtractExpression expr) { - expr.getExpression().accept(this); - } - - @Override - public void visit(IntervalExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(OracleHierarchicalExpression expr) { - constFlag.set(false); - } - - @Override - public void visit(RegExpMatchOperator expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(ExpressionList expressionList) { - for (Expression expr : expressionList.getExpressions()) { - expr.accept(this); - } - } - - @Override - public void visit(NamedExpressionList namedExpressionList) { - for (Expression expr : namedExpressionList.getExpressions()) { - expr.accept(this); - } - } - - @Override - public void visit(MultiExpressionList multiExprList) { - for (ExpressionList list : multiExprList.getExpressionLists()) { - visit(list); - } - } - - @Override - public void visit(NotExpression notExpr) { - notExpr.getExpression().accept(this); - } - - @Override - public void visit(BitwiseRightShift expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(BitwiseLeftShift expr) { - visitBinaryExpression(expr); - } - - protected void visitBinaryExpression(BinaryExpression expr) { - expr.getLeftExpression().accept(this); - expr.getRightExpression().accept(this); - } - - @Override - public void visit(JsonExpression jsonExpr) { - jsonExpr.getExpression().accept(this); - } - - @Override - public void visit(JsonOperator expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(RegExpMySQLOperator expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(UserVariable var) { - constFlag.set(false); - } - - @Override - public void visit(NumericBind bind) { - constFlag.set(false); - } - - @Override - public void visit(KeepExpression expr) { - for (OrderByElement element : expr.getOrderByElements()) { - element.getExpression().accept(this); - } - } - - @Override - public void visit(MySQLGroupConcat groupConcat) { - constFlag.set(false); - } - - @Override - public void visit(ValueListExpression valueListExpression) { - for (Expression expr : valueListExpression.getExpressionList().getExpressions()) { - expr.accept(this); - } - } - - @Override - public void visit(AllColumns allColumns) { - - } - - @Override - public void visit(AllTableColumns allTableColumns) { - - } - - @Override - public void visit(AllValue allValue) { - - } - - @Override - public void visit(IsDistinctExpression isDistinctExpression) { - visitBinaryExpression(isDistinctExpression); - } - - @Override - public void visit(RowGetExpression rowGetExpression) { - rowGetExpression.getExpression().accept(this); - } - - @Override - public void visit(HexValue hexValue) { - - } - - @Override - public void visit(OracleHint hint) { - - } - - @Override - public void visit(TimeKeyExpression timeKeyExpression) { - - } - - @Override - public void visit(DateTimeLiteralExpression literal) { - } - - @Override - public void visit(NextValExpression nextVal) { - constFlag.set(false); - } - - @Override - public void visit(CollateExpression col) { - constFlag.set(false); - } - - @Override - public void visit(SimilarToExpression expr) { - visitBinaryExpression(expr); - } - - @Override - public void visit(ArrayExpression array) { - array.getObjExpression().accept(this); - if (array.getIndexExpression() != null) { - array.getIndexExpression().accept(this); - } - if (array.getStartIndexExpression() != null) { - array.getStartIndexExpression().accept(this); - } - if (array.getStopIndexExpression() != null) { - array.getStopIndexExpression().accept(this); - } - } - - @Override - public void visit(ArrayConstructor aThis) { - for (Expression expression : aThis.getExpressions()) { - expression.accept(this); - } - } - - @Override - public void visit(VariableAssignment var) { - constFlag.set(false); - } - - @Override - public void visit(XMLSerializeExpr expr) { - constFlag.set(false); - } - - @Override - public void visit(TimezoneExpression expr) { - expr.getLeftExpression().accept(this); - } - - @Override - public void visit(JsonAggregateFunction expression) { - Expression expr = expression.getExpression(); - if (expr != null) { - expr.accept(this); - } - - expr = expression.getFilterExpression(); - if (expr != null) { - expr.accept(this); - } - } - - @Override - public void visit(JsonFunction expression) { - for (JsonFunctionExpression expr : expression.getExpressions()) { - expr.getExpression().accept(this); - } - } - - @Override - public void visit(ConnectByRootOperator connectByRootOperator) { - constFlag.set(false); - } - - @Override - public void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) { - constFlag.set(false); - } - - @Override - public void visit(GeometryDistance geometryDistance) { - visitBinaryExpression(geometryDistance); - } - - @Override - public void visit(RowConstructor rowConstructor) { - constFlag.set(false); - } - - public boolean isConstExpression(Expression expression) { - if (null != expression) { - constFlag.set(true); - expression.accept(this); - return constFlag.get(); - } - return false; - } -} - +//package org.jeecg.common.util.sqlInjection.parse; +// +//import net.sf.jsqlparser.expression.*; +//import net.sf.jsqlparser.expression.operators.arithmetic.Addition; +//import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseAnd; +//import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseLeftShift; +//import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseOr; +//import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseRightShift; +//import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor; +//import net.sf.jsqlparser.expression.operators.arithmetic.Concat; +//import net.sf.jsqlparser.expression.operators.arithmetic.Division; +//import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision; +//import net.sf.jsqlparser.expression.operators.arithmetic.Modulo; +//import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication; +//import net.sf.jsqlparser.expression.operators.arithmetic.Subtraction; +//import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +//import net.sf.jsqlparser.expression.operators.conditional.OrExpression; +//import net.sf.jsqlparser.expression.operators.conditional.XorExpression; +//import net.sf.jsqlparser.expression.operators.relational.Between; +//import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +//import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; +//import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +//import net.sf.jsqlparser.expression.operators.relational.FullTextSearch; +//import net.sf.jsqlparser.expression.operators.relational.GeometryDistance; +//import net.sf.jsqlparser.expression.operators.relational.GreaterThan; +//import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; +//import net.sf.jsqlparser.expression.operators.relational.InExpression; +//import net.sf.jsqlparser.expression.operators.relational.IsBooleanExpression; +//import net.sf.jsqlparser.expression.operators.relational.IsDistinctExpression; +//import net.sf.jsqlparser.expression.operators.relational.IsNullExpression; +//import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; +//import net.sf.jsqlparser.expression.operators.relational.JsonOperator; +//import net.sf.jsqlparser.expression.operators.relational.LikeExpression; +//import net.sf.jsqlparser.expression.operators.relational.Matches; +//import net.sf.jsqlparser.expression.operators.relational.MinorThan; +//import net.sf.jsqlparser.expression.operators.relational.MinorThanEquals; +//import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; +//import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; +//import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo; +//import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator; +//import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator; +//import net.sf.jsqlparser.expression.operators.relational.SimilarToExpression; +//import net.sf.jsqlparser.schema.Column; +//import net.sf.jsqlparser.statement.select.AllColumns; +//import net.sf.jsqlparser.statement.select.AllTableColumns; +//import net.sf.jsqlparser.statement.select.OrderByElement; +//import net.sf.jsqlparser.statement.select.SubSelect; +// +///** +// * 判断表达是否为常量的分析器 +// * +// * @author guyadong +// */ +//public class ConstAnalyzer implements ExpressionVisitor, ItemsListVisitor { +// +// private static ThreadLocal constFlag = new ThreadLocal() { +// @Override +// protected Boolean initialValue() { +// return true; +// } +// }; +// +// @Override +// public void visit(NullValue value) { +// } +// +// @Override +// public void visit(Function function) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(SignedExpression expr) { +// expr.getExpression().accept(this); +// } +// +// @Override +// public void visit(JdbcParameter parameter) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(JdbcNamedParameter parameter) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(DoubleValue value) { +// +// } +// +// @Override +// public void visit(LongValue value) { +// +// } +// +// @Override +// public void visit(DateValue value) { +// +// } +// +// @Override +// public void visit(TimeValue value) { +// +// } +// +// @Override +// public void visit(TimestampValue value) { +// +// } +// +// @Override +// public void visit(Parenthesis parenthesis) { +// parenthesis.getExpression().accept(this); +// } +// +// @Override +// public void visit(StringValue value) { +// +// } +// +// @Override +// public void visit(Addition expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(Division expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(IntegerDivision expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(Multiplication expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(Subtraction expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(AndExpression expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(OrExpression expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(XorExpression expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(Between expr) { +// expr.getLeftExpression().accept(this); +// expr.getBetweenExpressionStart().accept(this); +// expr.getBetweenExpressionEnd().accept(this); +// } +// +// /** +// * 用于处理 OverlapsCondition 类型的表达式 +// * @param overlapsCondition +// */ +// @Override +// public void visit(OverlapsCondition overlapsCondition) { +// constFlag.set(false); +// } +// /** +// * 用于处理 SafeCastExpression 类型的表达式。 +// * @param safeCastExpression +// */ +// @Override +// public void visit(SafeCastExpression safeCastExpression) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(EqualsTo expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(GreaterThan expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(GreaterThanEquals expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(InExpression expr) { +// if (expr.getLeftExpression() != null) { +// expr.getLeftExpression().accept(this); +// } +// } +// +// @Override +// public void visit(IsNullExpression expr) { +// expr.getLeftExpression().accept(this); +// } +// +// @Override +// public void visit(FullTextSearch expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(IsBooleanExpression expr) { +// expr.getLeftExpression().accept(this); +// } +// +// @Override +// public void visit(LikeExpression expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(MinorThan expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(MinorThanEquals expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(NotEqualsTo expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(Column column) { +// if (!ParserSupport.isBoolean(column)) { +// constFlag.set(false); +// } +// } +// +// @Override +// public void visit(SubSelect subSelect) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(CaseExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(WhenClause expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(ExistsExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(AnyComparisonExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(Concat expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(Matches expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(BitwiseAnd expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(BitwiseOr expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(BitwiseXor expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(CastExpression expr) { +// expr.getLeftExpression().accept(this); +// } +// +// @Override +// public void visit(TryCastExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(Modulo expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(AnalyticExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(ExtractExpression expr) { +// expr.getExpression().accept(this); +// } +// +// @Override +// public void visit(IntervalExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(OracleHierarchicalExpression expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(RegExpMatchOperator expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(ExpressionList expressionList) { +// for (Expression expr : expressionList.getExpressions()) { +// expr.accept(this); +// } +// } +// +// @Override +// public void visit(NamedExpressionList namedExpressionList) { +// for (Expression expr : namedExpressionList.getExpressions()) { +// expr.accept(this); +// } +// } +// +// @Override +// public void visit(MultiExpressionList multiExprList) { +// for (ExpressionList list : multiExprList.getExpressionLists()) { +// visit(list); +// } +// } +// +// @Override +// public void visit(NotExpression notExpr) { +// notExpr.getExpression().accept(this); +// } +// +// @Override +// public void visit(BitwiseRightShift expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(BitwiseLeftShift expr) { +// visitBinaryExpression(expr); +// } +// +// protected void visitBinaryExpression(BinaryExpression expr) { +// expr.getLeftExpression().accept(this); +// expr.getRightExpression().accept(this); +// } +// +// @Override +// public void visit(JsonExpression jsonExpr) { +// jsonExpr.getExpression().accept(this); +// } +// +// @Override +// public void visit(JsonOperator expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(RegExpMySQLOperator expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(UserVariable var) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(NumericBind bind) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(KeepExpression expr) { +// for (OrderByElement element : expr.getOrderByElements()) { +// element.getExpression().accept(this); +// } +// } +// +// @Override +// public void visit(MySQLGroupConcat groupConcat) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(ValueListExpression valueListExpression) { +// for (Expression expr : valueListExpression.getExpressionList().getExpressions()) { +// expr.accept(this); +// } +// } +// +// @Override +// public void visit(AllColumns allColumns) { +// +// } +// +// @Override +// public void visit(AllTableColumns allTableColumns) { +// +// } +// +// @Override +// public void visit(AllValue allValue) { +// +// } +// +// @Override +// public void visit(IsDistinctExpression isDistinctExpression) { +// visitBinaryExpression(isDistinctExpression); +// } +// +// @Override +// public void visit(RowGetExpression rowGetExpression) { +// rowGetExpression.getExpression().accept(this); +// } +// +// @Override +// public void visit(HexValue hexValue) { +// +// } +// +// @Override +// public void visit(OracleHint hint) { +// +// } +// +// @Override +// public void visit(TimeKeyExpression timeKeyExpression) { +// +// } +// +// @Override +// public void visit(DateTimeLiteralExpression literal) { +// } +// +// @Override +// public void visit(NextValExpression nextVal) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(CollateExpression col) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(SimilarToExpression expr) { +// visitBinaryExpression(expr); +// } +// +// @Override +// public void visit(ArrayExpression array) { +// array.getObjExpression().accept(this); +// if (array.getIndexExpression() != null) { +// array.getIndexExpression().accept(this); +// } +// if (array.getStartIndexExpression() != null) { +// array.getStartIndexExpression().accept(this); +// } +// if (array.getStopIndexExpression() != null) { +// array.getStopIndexExpression().accept(this); +// } +// } +// +// @Override +// public void visit(ArrayConstructor aThis) { +// for (Expression expression : aThis.getExpressions()) { +// expression.accept(this); +// } +// } +// +// @Override +// public void visit(VariableAssignment var) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(XMLSerializeExpr expr) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(TimezoneExpression expr) { +// expr.getLeftExpression().accept(this); +// } +// +// @Override +// public void visit(JsonAggregateFunction expression) { +// Expression expr = expression.getExpression(); +// if (expr != null) { +// expr.accept(this); +// } +// +// expr = expression.getFilterExpression(); +// if (expr != null) { +// expr.accept(this); +// } +// } +// +// @Override +// public void visit(JsonFunction expression) { +// for (JsonFunctionExpression expr : expression.getExpressions()) { +// expr.getExpression().accept(this); +// } +// } +// +// @Override +// public void visit(ConnectByRootOperator connectByRootOperator) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter) { +// constFlag.set(false); +// } +// +// @Override +// public void visit(GeometryDistance geometryDistance) { +// visitBinaryExpression(geometryDistance); +// } +// +// @Override +// public void visit(RowConstructor rowConstructor) { +// constFlag.set(false); +// } +// +// public boolean isConstExpression(Expression expression) { +// if (null != expression) { +// constFlag.set(true); +// expression.accept(this); +// return constFlag.get(); +// } +// return false; +// } +//} +// diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ParserSupport.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ParserSupport.java index 880872679..41613c98a 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ParserSupport.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/ParserSupport.java @@ -1,177 +1,177 @@ -package org.jeecg.common.util.sqlInjection.parse; - -import lombok.extern.slf4j.Slf4j; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.parser.*; -import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.select.PlainSelect; -import net.sf.jsqlparser.statement.select.Select; -import net.sf.jsqlparser.statement.select.SelectBody; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import java.lang.reflect.InvocationTargetException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.google.common.base.Throwables; -import org.jeecg.common.exception.JeecgBootException; -import org.jeecg.common.exception.JeecgSqlInjectionException; - -/** - * 解析sql支持 - */ -@Slf4j -public class ParserSupport { - /** - * 解析SELECT SQL语句,解析失败或非SELECT语句则抛出异常 - * - * @param sql - * @return - */ - public static Select parseSelect(String sql) { - Statement stmt; - try { - stmt = CCJSqlParserUtil.parse(checkNotNull(sql, "sql is null")); - } catch (JSQLParserException e) { - throw new JeecgBootException(e); - } - checkArgument(stmt instanceof Select, "%s is not SELECT statment", sql); - Select select = (Select) stmt; - SelectBody selectBody = select.getSelectBody(); - // 暂时只支持简单的SELECT xxxx FROM ....语句不支持复杂语句如WITH - checkArgument(selectBody instanceof PlainSelect, "ONLY SUPPORT plain select statement %s", sql); - return (Select) stmt; - } - - /** - * 解析SELECT SQL语句,解析失败或非SELECT语句则 - * - * @param sql - * @return - */ - public static Select parseSelectUnchecked(String sql) { - try { - return parseSelect(sql); - } catch (Exception e) { - return null; - } - } - - /** - * 实现SQL语句解析,解析成功则返回解析后的{@link Statement}, - * 并通过{@code visitor}参数提供基于AST(抽象语法树)的遍历所有节点的能力。 - * - * @param sql SQL语句 - * @param visitor 遍历所有节点的{@link SimpleNodeVisitor}接口实例,为{@code null}忽略 - * @param sqlSyntaxNormalizer SQL语句分析转换器,为{@code null}忽略 - * @throws JSQLParserException 输入的SQL语句有语法错误 - * @see #parse0(String, CCJSqlParserVisitor, SqlSyntaxNormalizer) - */ - public static Statement parse(String sql, CCJSqlParserVisitor visitor, SqlSyntaxNormalizer sqlSyntaxNormalizer) throws JSQLParserException { - return parse0(sql, visitor, sqlSyntaxNormalizer).statement; - } - - /** - * 参照{@link CCJSqlParserUtil#parseAST(String)}和{@link CCJSqlParserUtil#parse(String)}实现SQL语句解析, - * 解析成功则返回解析后的{@link SqlParserInfo}对象, - * 并通过{@code visitor}参数提供基于AST(抽象语法树)的遍历所有节点的能力。 - * - * @param sql SQL语句 - * @param visitor 遍历所有节点的{@link SimpleNodeVisitor}接口实例,为{@code null}忽略 - * @param sqlSyntaxAnalyzer SQL语句分析转换器,为{@code null}忽略 - * @throws JSQLParserException 输入的SQL语句有语法错误 - * @see net.sf.jsqlparser.parser.Node#jjtAccept(SimpleNodeVisitor, Object) - */ - public static SqlParserInfo parse0(String sql, CCJSqlParserVisitor visitor, SqlSyntaxNormalizer sqlSyntaxAnalyzer) throws JeecgSqlInjectionException { - - //检查是否非select开头,暂不支持 - if(!sql.toLowerCase().trim().startsWith("select ")) { - log.warn("传入sql 非select开头,不支持非select开头的语句解析!"); - return null; - } - - //检查是否存储过程,暂不支持 - if(sql.toLowerCase().trim().startsWith("call ")){ - log.warn("传入call 开头存储过程,不支持存储过程解析!"); - return null; - } - - //检查特殊语义的特殊字符,目前检查冒号、$、#三种特殊语义字符 - String specialCharacters = "[:$#]"; - Pattern pattern = Pattern.compile(specialCharacters); - Matcher matcher = pattern.matcher(sql); - if (matcher.find()) { - sql = sql.replaceAll("[:$#]", "@"); - } - - checkArgument(null != sql, "sql is null"); - boolean allowComplexParsing = CCJSqlParserUtil.getNestingDepth(sql) <= CCJSqlParserUtil.ALLOWED_NESTING_DEPTH; - - CCJSqlParser parser = CCJSqlParserUtil.newParser(sql).withAllowComplexParsing(allowComplexParsing); - Statement stmt; - try { - stmt = parser.Statement(); - } catch (Exception ex) { - log.error("请注意,SQL语法可能存在问题---> {}", ex.getMessage()); - throw new JeecgSqlInjectionException("请注意,SQL语法可能存在问题:"+sql); - } - if (null != visitor) { - parser.getASTRoot().jjtAccept(visitor, null); - } - if (null != sqlSyntaxAnalyzer) { - stmt.accept(sqlSyntaxAnalyzer.resetChanged()); - } - return new SqlParserInfo(stmt.toString(), stmt, (SimpleNode) parser.getASTRoot()); - } - - /** - * 调用{@link CCJSqlParser}解析SQL语句部件返回解析生成的对象,如{@code 'ORDER BY id DESC'} - * - * @param - * @param input - * @param method 指定调用的{@link CCJSqlParser}解析方法 - * @param targetType 返回的解析对象类型 - * @return - * @since 3.18.3 - */ - public static T parseComponent(String input, String method, Class targetType) { - try { - CCJSqlParser parser = new CCJSqlParser(new StringProvider(input)); - try { - return checkNotNull(targetType, "targetType is null").cast(parser.getClass().getMethod(method).invoke(parser)); - } catch (InvocationTargetException e) { - Throwables.throwIfUnchecked(e.getTargetException()); - throw new RuntimeException(e.getTargetException()); - } - } catch (IllegalAccessException | NoSuchMethodException | SecurityException e) { - Throwables.throwIfUnchecked(e); - throw new RuntimeException(e); - } - } - - /** - * 如果{@link Column}没有定义table,且字段名为true/false(不区分大小写)则视为布尔常量 - * - * @param column - */ - public static boolean isBoolean(Column column) { - return null != column && null == column.getTable() && - Pattern.compile("(true|false)", Pattern.CASE_INSENSITIVE).matcher(column.getColumnName()).matches(); - } - - public static class SqlParserInfo { - public String nativeSql; - public Statement statement; - public SimpleNode simpleNode; - - SqlParserInfo(String nativeSql, Statement statement, SimpleNode simpleNode) { - this.nativeSql = nativeSql; - this.statement = statement; - this.simpleNode = simpleNode; - } - } -} - +//package org.jeecg.common.util.sqlInjection.parse; +// +//import lombok.extern.slf4j.Slf4j; +//import net.sf.jsqlparser.JSQLParserException; +//import net.sf.jsqlparser.parser.*; +//import net.sf.jsqlparser.schema.Column; +//import net.sf.jsqlparser.statement.Statement; +//import net.sf.jsqlparser.statement.select.PlainSelect; +//import net.sf.jsqlparser.statement.select.Select; +//import net.sf.jsqlparser.statement.select.SelectBody; +// +//import static com.google.common.base.Preconditions.checkArgument; +//import static com.google.common.base.Preconditions.checkNotNull; +// +//import java.lang.reflect.InvocationTargetException; +//import java.util.regex.Matcher; +//import java.util.regex.Pattern; +// +//import com.google.common.base.Throwables; +//import org.jeecg.common.exception.JeecgBootException; +//import org.jeecg.common.exception.JeecgSqlInjectionException; +// +///** +// * 解析sql支持 +// */ +//@Slf4j +//public class ParserSupport { +// /** +// * 解析SELECT SQL语句,解析失败或非SELECT语句则抛出异常 +// * +// * @param sql +// * @return +// */ +// public static Select parseSelect(String sql) { +// Statement stmt; +// try { +// stmt = CCJSqlParserUtil.parse(checkNotNull(sql, "sql is null")); +// } catch (JSQLParserException e) { +// throw new JeecgBootException(e); +// } +// checkArgument(stmt instanceof Select, "%s is not SELECT statment", sql); +// Select select = (Select) stmt; +// SelectBody selectBody = select.getSelectBody(); +// // 暂时只支持简单的SELECT xxxx FROM ....语句不支持复杂语句如WITH +// checkArgument(selectBody instanceof PlainSelect, "ONLY SUPPORT plain select statement %s", sql); +// return (Select) stmt; +// } +// +// /** +// * 解析SELECT SQL语句,解析失败或非SELECT语句则 +// * +// * @param sql +// * @return +// */ +// public static Select parseSelectUnchecked(String sql) { +// try { +// return parseSelect(sql); +// } catch (Exception e) { +// return null; +// } +// } +// +// /** +// * 实现SQL语句解析,解析成功则返回解析后的{@link Statement}, +// * 并通过{@code visitor}参数提供基于AST(抽象语法树)的遍历所有节点的能力。 +// * +// * @param sql SQL语句 +// * @param visitor 遍历所有节点的{@link SimpleNodeVisitor}接口实例,为{@code null}忽略 +// * @param sqlSyntaxNormalizer SQL语句分析转换器,为{@code null}忽略 +// * @throws JSQLParserException 输入的SQL语句有语法错误 +// * @see #parse0(String, CCJSqlParserVisitor, SqlSyntaxNormalizer) +// */ +// public static Statement parse(String sql, CCJSqlParserVisitor visitor, SqlSyntaxNormalizer sqlSyntaxNormalizer) throws JSQLParserException { +// return parse0(sql, visitor, sqlSyntaxNormalizer).statement; +// } +// +// /** +// * 参照{@link CCJSqlParserUtil#parseAST(String)}和{@link CCJSqlParserUtil#parse(String)}实现SQL语句解析, +// * 解析成功则返回解析后的{@link SqlParserInfo}对象, +// * 并通过{@code visitor}参数提供基于AST(抽象语法树)的遍历所有节点的能力。 +// * +// * @param sql SQL语句 +// * @param visitor 遍历所有节点的{@link SimpleNodeVisitor}接口实例,为{@code null}忽略 +// * @param sqlSyntaxAnalyzer SQL语句分析转换器,为{@code null}忽略 +// * @throws JSQLParserException 输入的SQL语句有语法错误 +// * @see net.sf.jsqlparser.parser.Node#jjtAccept(SimpleNodeVisitor, Object) +// */ +// public static SqlParserInfo parse0(String sql, CCJSqlParserVisitor visitor, SqlSyntaxNormalizer sqlSyntaxAnalyzer) throws JeecgSqlInjectionException { +// +// //检查是否非select开头,暂不支持 +// if(!sql.toLowerCase().trim().startsWith("select ")) { +// log.warn("传入sql 非select开头,不支持非select开头的语句解析!"); +// return null; +// } +// +// //检查是否存储过程,暂不支持 +// if(sql.toLowerCase().trim().startsWith("call ")){ +// log.warn("传入call 开头存储过程,不支持存储过程解析!"); +// return null; +// } +// +// //检查特殊语义的特殊字符,目前检查冒号、$、#三种特殊语义字符 +// String specialCharacters = "[:$#]"; +// Pattern pattern = Pattern.compile(specialCharacters); +// Matcher matcher = pattern.matcher(sql); +// if (matcher.find()) { +// sql = sql.replaceAll("[:$#]", "@"); +// } +// +// checkArgument(null != sql, "sql is null"); +// boolean allowComplexParsing = CCJSqlParserUtil.getNestingDepth(sql) <= CCJSqlParserUtil.ALLOWED_NESTING_DEPTH; +// +// CCJSqlParser parser = CCJSqlParserUtil.newParser(sql).withAllowComplexParsing(allowComplexParsing); +// Statement stmt; +// try { +// stmt = parser.Statement(); +// } catch (Exception ex) { +// log.error("请注意,SQL语法可能存在问题---> {}", ex.getMessage()); +// throw new JeecgSqlInjectionException("请注意,SQL语法可能存在问题:"+sql); +// } +// if (null != visitor) { +// parser.getASTRoot().jjtAccept(visitor, null); +// } +// if (null != sqlSyntaxAnalyzer) { +// stmt.accept(sqlSyntaxAnalyzer.resetChanged()); +// } +// return new SqlParserInfo(stmt.toString(), stmt, (SimpleNode) parser.getASTRoot()); +// } +// +// /** +// * 调用{@link CCJSqlParser}解析SQL语句部件返回解析生成的对象,如{@code 'ORDER BY id DESC'} +// * +// * @param +// * @param input +// * @param method 指定调用的{@link CCJSqlParser}解析方法 +// * @param targetType 返回的解析对象类型 +// * @return +// * @since 3.18.3 +// */ +// public static T parseComponent(String input, String method, Class targetType) { +// try { +// CCJSqlParser parser = new CCJSqlParser(new StringProvider(input)); +// try { +// return checkNotNull(targetType, "targetType is null").cast(parser.getClass().getMethod(method).invoke(parser)); +// } catch (InvocationTargetException e) { +// Throwables.throwIfUnchecked(e.getTargetException()); +// throw new RuntimeException(e.getTargetException()); +// } +// } catch (IllegalAccessException | NoSuchMethodException | SecurityException e) { +// Throwables.throwIfUnchecked(e); +// throw new RuntimeException(e); +// } +// } +// +// /** +// * 如果{@link Column}没有定义table,且字段名为true/false(不区分大小写)则视为布尔常量 +// * +// * @param column +// */ +// public static boolean isBoolean(Column column) { +// return null != column && null == column.getTable() && +// Pattern.compile("(true|false)", Pattern.CASE_INSENSITIVE).matcher(column.getColumnName()).matches(); +// } +// +// public static class SqlParserInfo { +// public String nativeSql; +// public Statement statement; +// public SimpleNode simpleNode; +// +// SqlParserInfo(String nativeSql, Statement statement, SimpleNode simpleNode) { +// this.nativeSql = nativeSql; +// this.statement = statement; +// this.simpleNode = simpleNode; +// } +// } +//} +// diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/SqlSyntaxNormalizer.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/SqlSyntaxNormalizer.java index 844478197..291a39a5e 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/SqlSyntaxNormalizer.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlInjection/parse/SqlSyntaxNormalizer.java @@ -1,37 +1,37 @@ -package org.jeecg.common.util.sqlInjection.parse; - -import net.sf.jsqlparser.util.TablesNamesFinder; - -/** - * SQL语句分析转换器基类
- * 基于SQL语法对象实现对SQL的修改 - * (暂时用不到) - * - * @author guyadong - * @since 3.17.0 - */ -public class SqlSyntaxNormalizer extends TablesNamesFinder { - protected static final ThreadLocal changed = new ThreadLocal<>(); - - public SqlSyntaxNormalizer() { - super(); - init(true); - - } - - /** - * 语句改变返回{@code true},否则返回{@code false} - */ - public boolean changed() { - return Boolean.TRUE.equals(changed.get()); - } - - /** - * 复位线程局部变量{@link #changed}状态 - */ - public SqlSyntaxNormalizer resetChanged() { - changed.remove(); - return this; - } -} - +//package org.jeecg.common.util.sqlInjection.parse; +// +//import net.sf.jsqlparser.util.TablesNamesFinder; +// +///** +// * SQL语句分析转换器基类
+// * 基于SQL语法对象实现对SQL的修改 +// * (暂时用不到) +// * +// * @author guyadong +// * @since 3.17.0 +// */ +//public class SqlSyntaxNormalizer extends TablesNamesFinder { +// protected static final ThreadLocal changed = new ThreadLocal<>(); +// +// public SqlSyntaxNormalizer() { +// super(); +// init(true); +// +// } +// +// /** +// * 语句改变返回{@code true},否则返回{@code false} +// */ +// public boolean changed() { +// return Boolean.TRUE.equals(changed.get()); +// } +// +// /** +// * 复位线程局部变量{@link #changed}状态 +// */ +// public SqlSyntaxNormalizer resetChanged() { +// changed.remove(); +// return this; +// } +//} +// diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserAllTableManager.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserAllTableManager.java index 1482a0412..ce6eefd26 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserAllTableManager.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserAllTableManager.java @@ -1,255 +1,255 @@ -package org.jeecg.common.util.sqlparse; - -import lombok.extern.slf4j.Slf4j; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.expression.*; -import net.sf.jsqlparser.parser.CCJSqlParserManager; -import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.schema.Table; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.select.*; -import org.jeecg.common.exception.JeecgBootException; -import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; - -import java.io.StringReader; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * 解析所有表名和字段的类 - */ -@Slf4j -public class JSqlParserAllTableManager { - - private final String sql; - private final Map allTableMap = new HashMap<>(); - /** - * 别名对应实际表名 - */ - private final Map tableAliasMap = new HashMap<>(); - - /** - * 解析后的sql - */ - private String parsedSql = null; - - JSqlParserAllTableManager(String selectSql) { - this.sql = selectSql; - } - - /** - * 开始解析 - * - * @return - * @throws JSQLParserException - */ - public Map parse() throws JSQLParserException { - // 1. 创建解析器 - CCJSqlParserManager mgr = new CCJSqlParserManager(); - // 2. 使用解析器解析sql生成具有层次结构的java类 - Statement stmt = mgr.parse(new StringReader(this.sql)); - if (stmt instanceof Select) { - Select selectStatement = (Select) stmt; - SelectBody selectBody = selectStatement.getSelectBody(); - this.parsedSql = selectBody.toString(); - // 3. 解析select查询sql的信息 - if (selectBody instanceof PlainSelect) { - PlainSelect plainSelect = (PlainSelect) selectBody; - // 4. 合并 fromItems - List fromItems = new ArrayList<>(); - fromItems.add(plainSelect.getFromItem()); - // 4.1 处理join的表 - List joins = plainSelect.getJoins(); - if (joins != null) { - joins.forEach(join -> fromItems.add(join.getRightItem())); - } - // 5. 处理 fromItems - for (FromItem fromItem : fromItems) { - // 5.1 通过表名的方式from - if (fromItem instanceof Table) { - this.addSqlInfoByTable((Table) fromItem); - } - // 5.2 通过子查询的方式from - else if (fromItem instanceof SubSelect) { - this.handleSubSelect((SubSelect) fromItem); - } - } - // 6. 解析 selectFields - List selectItems = plainSelect.getSelectItems(); - for (SelectItem selectItem : selectItems) { - // 6.1 查询的是全部字段 - if (selectItem instanceof AllColumns) { - // 当 selectItem 为 AllColumns 时,fromItem 必定为 Table - String tableName = plainSelect.getFromItem(Table.class).getName(); - // 此处必定不为空,因为在解析 fromItem 时,已经将表名添加到 allTableMap 中 - SelectSqlInfo sqlInfo = this.allTableMap.get(tableName); - assert sqlInfo != null; - // 设置为查询全部字段 - sqlInfo.setSelectAll(true); - sqlInfo.setSelectFields(null); - sqlInfo.setRealSelectFields(null); - } - // 6.2 查询的是带表别名( u.* )的全部字段 - else if (selectItem instanceof AllTableColumns) { - AllTableColumns allTableColumns = (AllTableColumns) selectItem; - String aliasName = allTableColumns.getTable().getName(); - // 通过别名获取表名 - String tableName = this.tableAliasMap.get(aliasName); - if (tableName == null) { - tableName = aliasName; - } - SelectSqlInfo sqlInfo = this.allTableMap.get(tableName); - // 如果此处为空,则说明该字段是通过子查询获取的,所以可以不处理,只有实际表才需要处理 - if (sqlInfo != null) { - // 设置为查询全部字段 - sqlInfo.setSelectAll(true); - sqlInfo.setSelectFields(null); - sqlInfo.setRealSelectFields(null); - } - } - // 6.3 各种字段表达式处理 - else if (selectItem instanceof SelectExpressionItem) { - SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; - Expression expression = selectExpressionItem.getExpression(); - Alias alias = selectExpressionItem.getAlias(); - this.handleExpression(expression, alias, plainSelect.getFromItem()); - } - } - } else { - log.warn("暂时尚未处理该类型的 SelectBody: {}", selectBody.getClass().getName()); - throw new JeecgBootException("暂时尚未处理该类型的 SelectBody"); - } - } else { - // 非 select 查询sql,不做处理 - throw new JeecgBootException("非 select 查询sql,不做处理"); - } - return this.allTableMap; - } - - /** - * 处理子查询 - * - * @param subSelect - */ - private void handleSubSelect(SubSelect subSelect) { - try { - String subSelectSql = subSelect.getSelectBody().toString(); - // 递归调用解析 - Map map = JSqlParserUtils.parseAllSelectTable(subSelectSql); - if (map != null) { - this.assignMap(map); - } - } catch (Exception e) { - log.error("解析子查询出错", e); - } - } - - /** - * 处理查询字段表达式 - * - * @param expression - */ - private void handleExpression(Expression expression, Alias alias, FromItem fromItem) { - // 处理函数式字段 CONCAT(name,'(',age,')') - if (expression instanceof Function) { - Function functionExp = (Function) expression; - List expressions = functionExp.getParameters().getExpressions(); - for (Expression expItem : expressions) { - this.handleExpression(expItem, null, fromItem); - } - return; - } - // 处理字段上的子查询 - if (expression instanceof SubSelect) { - this.handleSubSelect((SubSelect) expression); - return; - } - // 不处理字面量 - if (expression instanceof StringValue || - expression instanceof NullValue || - expression instanceof LongValue || - expression instanceof DoubleValue || - expression instanceof HexValue || - expression instanceof DateValue || - expression instanceof TimestampValue || - expression instanceof TimeValue - ) { - return; - } - - // 处理字段 - if (expression instanceof Column) { - Column column = (Column) expression; - // 查询字段名 - String fieldName = column.getColumnName(); - String aliasName = fieldName; - if (alias != null) { - aliasName = alias.getName(); - } - String tableName; - if (column.getTable() != null) { - // 通过列的表名获取 sqlInfo - // 例如 user.name,这里的 tableName 就是 user - tableName = column.getTable().getName(); - // 有可能是别名,需要转换为真实表名 - if (this.tableAliasMap.get(tableName) != null) { - tableName = this.tableAliasMap.get(tableName); - } - } else { - // 当column的table为空时,说明是 fromItem 中的字段 - tableName = ((Table) fromItem).getName(); - } - SelectSqlInfo $sqlInfo = this.allTableMap.get(tableName); - if ($sqlInfo != null) { - $sqlInfo.addSelectField(aliasName, fieldName); - } else { - log.warn("发生意外情况,未找到表名为 {} 的 SelectSqlInfo", tableName); - } - } - } - - /** - * 根据表名添加sqlInfo - * - * @param table - */ - private void addSqlInfoByTable(Table table) { - String tableName = table.getName(); - // 解析 aliasName - if (table.getAlias() != null) { - this.tableAliasMap.put(table.getAlias().getName(), tableName); - } - SelectSqlInfo sqlInfo = new SelectSqlInfo(this.parsedSql); - sqlInfo.setFromTableName(table.getName()); - this.allTableMap.put(sqlInfo.getFromTableName(), sqlInfo); - } - - /** - * 合并map - * - * @param source - */ - private void assignMap(Map source) { - for (Map.Entry entry : source.entrySet()) { - SelectSqlInfo sqlInfo = this.allTableMap.get(entry.getKey()); - if (sqlInfo == null) { - this.allTableMap.put(entry.getKey(), entry.getValue()); - } else { - // 合并 - if (sqlInfo.getSelectFields() == null) { - sqlInfo.setSelectFields(entry.getValue().getSelectFields()); - } else { - sqlInfo.getSelectFields().addAll(entry.getValue().getSelectFields()); - } - if (sqlInfo.getRealSelectFields() == null) { - sqlInfo.setRealSelectFields(entry.getValue().getRealSelectFields()); - } else { - sqlInfo.getRealSelectFields().addAll(entry.getValue().getRealSelectFields()); - } - } - } - } - -} +//package org.jeecg.common.util.sqlparse; +// +//import lombok.extern.slf4j.Slf4j; +//import net.sf.jsqlparser.JSQLParserException; +//import net.sf.jsqlparser.expression.*; +//import net.sf.jsqlparser.parser.CCJSqlParserManager; +//import net.sf.jsqlparser.schema.Column; +//import net.sf.jsqlparser.schema.Table; +//import net.sf.jsqlparser.statement.Statement; +//import net.sf.jsqlparser.statement.select.*; +//import org.jeecg.common.exception.JeecgBootException; +//import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; +// +//import java.io.StringReader; +//import java.util.ArrayList; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +// +///** +// * 解析所有表名和字段的类 +// */ +//@Slf4j +//public class JSqlParserAllTableManager { +// +// private final String sql; +// private final Map allTableMap = new HashMap<>(); +// /** +// * 别名对应实际表名 +// */ +// private final Map tableAliasMap = new HashMap<>(); +// +// /** +// * 解析后的sql +// */ +// private String parsedSql = null; +// +// JSqlParserAllTableManager(String selectSql) { +// this.sql = selectSql; +// } +// +// /** +// * 开始解析 +// * +// * @return +// * @throws JSQLParserException +// */ +// public Map parse() throws JSQLParserException { +// // 1. 创建解析器 +// CCJSqlParserManager mgr = new CCJSqlParserManager(); +// // 2. 使用解析器解析sql生成具有层次结构的java类 +// Statement stmt = mgr.parse(new StringReader(this.sql)); +// if (stmt instanceof Select) { +// Select selectStatement = (Select) stmt; +// SelectBody selectBody = selectStatement.getSelectBody(); +// this.parsedSql = selectBody.toString(); +// // 3. 解析select查询sql的信息 +// if (selectBody instanceof PlainSelect) { +// PlainSelect plainSelect = (PlainSelect) selectBody; +// // 4. 合并 fromItems +// List fromItems = new ArrayList<>(); +// fromItems.add(plainSelect.getFromItem()); +// // 4.1 处理join的表 +// List joins = plainSelect.getJoins(); +// if (joins != null) { +// joins.forEach(join -> fromItems.add(join.getRightItem())); +// } +// // 5. 处理 fromItems +// for (FromItem fromItem : fromItems) { +// // 5.1 通过表名的方式from +// if (fromItem instanceof Table) { +// this.addSqlInfoByTable((Table) fromItem); +// } +// // 5.2 通过子查询的方式from +// else if (fromItem instanceof SubSelect) { +// this.handleSubSelect((SubSelect) fromItem); +// } +// } +// // 6. 解析 selectFields +// List selectItems = plainSelect.getSelectItems(); +// for (SelectItem selectItem : selectItems) { +// // 6.1 查询的是全部字段 +// if (selectItem instanceof AllColumns) { +// // 当 selectItem 为 AllColumns 时,fromItem 必定为 Table +// String tableName = plainSelect.getFromItem(Table.class).getName(); +// // 此处必定不为空,因为在解析 fromItem 时,已经将表名添加到 allTableMap 中 +// SelectSqlInfo sqlInfo = this.allTableMap.get(tableName); +// assert sqlInfo != null; +// // 设置为查询全部字段 +// sqlInfo.setSelectAll(true); +// sqlInfo.setSelectFields(null); +// sqlInfo.setRealSelectFields(null); +// } +// // 6.2 查询的是带表别名( u.* )的全部字段 +// else if (selectItem instanceof AllTableColumns) { +// AllTableColumns allTableColumns = (AllTableColumns) selectItem; +// String aliasName = allTableColumns.getTable().getName(); +// // 通过别名获取表名 +// String tableName = this.tableAliasMap.get(aliasName); +// if (tableName == null) { +// tableName = aliasName; +// } +// SelectSqlInfo sqlInfo = this.allTableMap.get(tableName); +// // 如果此处为空,则说明该字段是通过子查询获取的,所以可以不处理,只有实际表才需要处理 +// if (sqlInfo != null) { +// // 设置为查询全部字段 +// sqlInfo.setSelectAll(true); +// sqlInfo.setSelectFields(null); +// sqlInfo.setRealSelectFields(null); +// } +// } +// // 6.3 各种字段表达式处理 +// else if (selectItem instanceof SelectExpressionItem) { +// SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; +// Expression expression = selectExpressionItem.getExpression(); +// Alias alias = selectExpressionItem.getAlias(); +// this.handleExpression(expression, alias, plainSelect.getFromItem()); +// } +// } +// } else { +// log.warn("暂时尚未处理该类型的 SelectBody: {}", selectBody.getClass().getName()); +// throw new JeecgBootException("暂时尚未处理该类型的 SelectBody"); +// } +// } else { +// // 非 select 查询sql,不做处理 +// throw new JeecgBootException("非 select 查询sql,不做处理"); +// } +// return this.allTableMap; +// } +// +// /** +// * 处理子查询 +// * +// * @param subSelect +// */ +// private void handleSubSelect(SubSelect subSelect) { +// try { +// String subSelectSql = subSelect.getSelectBody().toString(); +// // 递归调用解析 +// Map map = JSqlParserUtils.parseAllSelectTable(subSelectSql); +// if (map != null) { +// this.assignMap(map); +// } +// } catch (Exception e) { +// log.error("解析子查询出错", e); +// } +// } +// +// /** +// * 处理查询字段表达式 +// * +// * @param expression +// */ +// private void handleExpression(Expression expression, Alias alias, FromItem fromItem) { +// // 处理函数式字段 CONCAT(name,'(',age,')') +// if (expression instanceof Function) { +// Function functionExp = (Function) expression; +// List expressions = functionExp.getParameters().getExpressions(); +// for (Expression expItem : expressions) { +// this.handleExpression(expItem, null, fromItem); +// } +// return; +// } +// // 处理字段上的子查询 +// if (expression instanceof SubSelect) { +// this.handleSubSelect((SubSelect) expression); +// return; +// } +// // 不处理字面量 +// if (expression instanceof StringValue || +// expression instanceof NullValue || +// expression instanceof LongValue || +// expression instanceof DoubleValue || +// expression instanceof HexValue || +// expression instanceof DateValue || +// expression instanceof TimestampValue || +// expression instanceof TimeValue +// ) { +// return; +// } +// +// // 处理字段 +// if (expression instanceof Column) { +// Column column = (Column) expression; +// // 查询字段名 +// String fieldName = column.getColumnName(); +// String aliasName = fieldName; +// if (alias != null) { +// aliasName = alias.getName(); +// } +// String tableName; +// if (column.getTable() != null) { +// // 通过列的表名获取 sqlInfo +// // 例如 user.name,这里的 tableName 就是 user +// tableName = column.getTable().getName(); +// // 有可能是别名,需要转换为真实表名 +// if (this.tableAliasMap.get(tableName) != null) { +// tableName = this.tableAliasMap.get(tableName); +// } +// } else { +// // 当column的table为空时,说明是 fromItem 中的字段 +// tableName = ((Table) fromItem).getName(); +// } +// SelectSqlInfo $sqlInfo = this.allTableMap.get(tableName); +// if ($sqlInfo != null) { +// $sqlInfo.addSelectField(aliasName, fieldName); +// } else { +// log.warn("发生意外情况,未找到表名为 {} 的 SelectSqlInfo", tableName); +// } +// } +// } +// +// /** +// * 根据表名添加sqlInfo +// * +// * @param table +// */ +// private void addSqlInfoByTable(Table table) { +// String tableName = table.getName(); +// // 解析 aliasName +// if (table.getAlias() != null) { +// this.tableAliasMap.put(table.getAlias().getName(), tableName); +// } +// SelectSqlInfo sqlInfo = new SelectSqlInfo(this.parsedSql); +// sqlInfo.setFromTableName(table.getName()); +// this.allTableMap.put(sqlInfo.getFromTableName(), sqlInfo); +// } +// +// /** +// * 合并map +// * +// * @param source +// */ +// private void assignMap(Map source) { +// for (Map.Entry entry : source.entrySet()) { +// SelectSqlInfo sqlInfo = this.allTableMap.get(entry.getKey()); +// if (sqlInfo == null) { +// this.allTableMap.put(entry.getKey(), entry.getValue()); +// } else { +// // 合并 +// if (sqlInfo.getSelectFields() == null) { +// sqlInfo.setSelectFields(entry.getValue().getSelectFields()); +// } else { +// sqlInfo.getSelectFields().addAll(entry.getValue().getSelectFields()); +// } +// if (sqlInfo.getRealSelectFields() == null) { +// sqlInfo.setRealSelectFields(entry.getValue().getRealSelectFields()); +// } else { +// sqlInfo.getRealSelectFields().addAll(entry.getValue().getRealSelectFields()); +// } +// } +// } +// } +// +//} diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserUtils.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserUtils.java index 01373e4ff..e8788b718 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserUtils.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/JSqlParserUtils.java @@ -1,190 +1,190 @@ -package org.jeecg.common.util.sqlparse; - -import lombok.extern.slf4j.Slf4j; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.expression.*; -import net.sf.jsqlparser.parser.CCJSqlParserManager; -import net.sf.jsqlparser.schema.Column; -import net.sf.jsqlparser.schema.Table; -import net.sf.jsqlparser.statement.Statement; -import net.sf.jsqlparser.statement.select.*; -import org.jeecg.common.exception.JeecgBootException; -import org.jeecg.common.util.oConvertUtils; -import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; - -import java.io.StringReader; -import java.util.List; -import java.util.Map; - -@Slf4j -public class JSqlParserUtils { - - /** - * 解析 查询(select)sql的信息, - * 此方法会展开所有子查询到一个map里, - * key只存真实的表名,如果查询的没有真实的表名,则会被忽略。 - * value只存真实的字段名,如果查询的没有真实的字段名,则会被忽略。 - *

- * 例如:SELECT a.*,d.age,(SELECT count(1) FROM sys_depart) AS count FROM (SELECT username AS foo, realname FROM sys_user) a, demo d - * 解析后的结果为:{sys_user=[username, realname], demo=[age], sys_depart=[]} - * - * @param selectSql - * @return - */ - public static Map parseAllSelectTable(String selectSql) throws JSQLParserException { - if (oConvertUtils.isEmpty(selectSql)) { - return null; - } - // log.info("解析查询Sql:{}", selectSql); - JSqlParserAllTableManager allTableManager = new JSqlParserAllTableManager(selectSql); - return allTableManager.parse(); - } - - /** - * 解析 查询(select)sql的信息,子查询嵌套 - * - * @param selectSql - * @return - */ - public static SelectSqlInfo parseSelectSqlInfo(String selectSql) throws JSQLParserException { - if (oConvertUtils.isEmpty(selectSql)) { - return null; - } - // log.info("解析查询Sql:{}", selectSql); - // 使用 JSqlParer 解析sql - // 1、创建解析器 - CCJSqlParserManager mgr = new CCJSqlParserManager(); - // 2、使用解析器解析sql生成具有层次结构的java类 - Statement stmt = mgr.parse(new StringReader(selectSql)); - if (stmt instanceof Select) { - Select selectStatement = (Select) stmt; - // 3、解析select查询sql的信息 - return JSqlParserUtils.parseBySelectBody(selectStatement.getSelectBody()); - } else { - // 非 select 查询sql,不做处理 - throw new JeecgBootException("非 select 查询sql,不做处理"); - } - } - - /** - * 解析 select 查询sql的信息 - * - * @param selectBody - * @return - */ - private static SelectSqlInfo parseBySelectBody(SelectBody selectBody) { - // 判断是否使用了union等操作 - if (selectBody instanceof SetOperationList) { - // 如果使用了union等操作,则只解析第一个查询 - List selectBodyList = ((SetOperationList) selectBody).getSelects(); - return JSqlParserUtils.parseBySelectBody(selectBodyList.get(0)); - } - // 简单的select查询 - if (selectBody instanceof PlainSelect) { - SelectSqlInfo sqlInfo = new SelectSqlInfo(selectBody); - PlainSelect plainSelect = (PlainSelect) selectBody; - FromItem fromItem = plainSelect.getFromItem(); - // 解析 aliasName - if (fromItem.getAlias() != null) { - sqlInfo.setFromTableAliasName(fromItem.getAlias().getName()); - } - // 解析 表名 - if (fromItem instanceof Table) { - // 通过表名的方式from - Table fromTable = (Table) fromItem; - sqlInfo.setFromTableName(fromTable.getName()); - } else if (fromItem instanceof SubSelect) { - // 通过子查询的方式from - SubSelect fromSubSelect = (SubSelect) fromItem; - SelectSqlInfo subSqlInfo = JSqlParserUtils.parseBySelectBody(fromSubSelect.getSelectBody()); - sqlInfo.setFromSubSelect(subSqlInfo); - } - // 解析 selectFields - List selectItems = plainSelect.getSelectItems(); - for (SelectItem selectItem : selectItems) { - if (selectItem instanceof AllColumns || selectItem instanceof AllTableColumns) { - // 全部字段 - sqlInfo.setSelectAll(true); - sqlInfo.setSelectFields(null); - sqlInfo.setRealSelectFields(null); - break; - } else if (selectItem instanceof SelectExpressionItem) { - // 获取单个查询字段名 - SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; - Expression expression = selectExpressionItem.getExpression(); - Alias alias = selectExpressionItem.getAlias(); - JSqlParserUtils.handleExpression(sqlInfo, expression, alias); - } - } - return sqlInfo; - } else { - log.warn("暂时尚未处理该类型的 SelectBody: {}", selectBody.getClass().getName()); - throw new JeecgBootException("暂时尚未处理该类型的 SelectBody"); - } - } - - /** - * 处理查询字段表达式 - * - * @param sqlInfo - * @param expression - * @param alias 是否有别名,无传null - */ - private static void handleExpression(SelectSqlInfo sqlInfo, Expression expression, Alias alias) { - // 处理函数式字段 CONCAT(name,'(',age,')') - if (expression instanceof Function) { - JSqlParserUtils.handleFunctionExpression((Function) expression, sqlInfo); - return; - } - // 处理字段上的子查询 - if (expression instanceof SubSelect) { - SubSelect subSelect = (SubSelect) expression; - SelectSqlInfo subSqlInfo = JSqlParserUtils.parseBySelectBody(subSelect.getSelectBody()); - // 注:字段上的子查询,必须只查询一个字段,否则会报错,所以可以放心合并 - sqlInfo.getSelectFields().addAll(subSqlInfo.getSelectFields()); - sqlInfo.getRealSelectFields().addAll(subSqlInfo.getAllRealSelectFields()); - return; - } - // 不处理字面量 - if (expression instanceof StringValue || - expression instanceof NullValue || - expression instanceof LongValue || - expression instanceof DoubleValue || - expression instanceof HexValue || - expression instanceof DateValue || - expression instanceof TimestampValue || - expression instanceof TimeValue - ) { - return; - } - - // 查询字段名 - String selectField = expression.toString(); - // 实际查询字段名 - String realSelectField = selectField; - // 判断是否有别名 - if (alias != null) { - selectField = alias.getName(); - } - // 获取真实字段名 - if (expression instanceof Column) { - Column column = (Column) expression; - realSelectField = column.getColumnName(); - } - sqlInfo.addSelectField(selectField, realSelectField); - } - - /** - * 处理函数式字段 - * - * @param functionExp - * @param sqlInfo - */ - private static void handleFunctionExpression(Function functionExp, SelectSqlInfo sqlInfo) { - List expressions = functionExp.getParameters().getExpressions(); - for (Expression expression : expressions) { - JSqlParserUtils.handleExpression(sqlInfo, expression, null); - } - } - -} +//package org.jeecg.common.util.sqlparse; +// +//import lombok.extern.slf4j.Slf4j; +//import net.sf.jsqlparser.JSQLParserException; +//import net.sf.jsqlparser.expression.*; +//import net.sf.jsqlparser.parser.CCJSqlParserManager; +//import net.sf.jsqlparser.schema.Column; +//import net.sf.jsqlparser.schema.Table; +//import net.sf.jsqlparser.statement.Statement; +//import net.sf.jsqlparser.statement.select.*; +//import org.jeecg.common.exception.JeecgBootException; +//import org.jeecg.common.util.oConvertUtils; +//import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; +// +//import java.io.StringReader; +//import java.util.List; +//import java.util.Map; +// +//@Slf4j +//public class JSqlParserUtils { +// +// /** +// * 解析 查询(select)sql的信息, +// * 此方法会展开所有子查询到一个map里, +// * key只存真实的表名,如果查询的没有真实的表名,则会被忽略。 +// * value只存真实的字段名,如果查询的没有真实的字段名,则会被忽略。 +// *

+// * 例如:SELECT a.*,d.age,(SELECT count(1) FROM sys_depart) AS count FROM (SELECT username AS foo, realname FROM sys_user) a, demo d +// * 解析后的结果为:{sys_user=[username, realname], demo=[age], sys_depart=[]} +// * +// * @param selectSql +// * @return +// */ +// public static Map parseAllSelectTable(String selectSql) throws JSQLParserException { +// if (oConvertUtils.isEmpty(selectSql)) { +// return null; +// } +// // log.info("解析查询Sql:{}", selectSql); +// JSqlParserAllTableManager allTableManager = new JSqlParserAllTableManager(selectSql); +// return allTableManager.parse(); +// } +// +// /** +// * 解析 查询(select)sql的信息,子查询嵌套 +// * +// * @param selectSql +// * @return +// */ +// public static SelectSqlInfo parseSelectSqlInfo(String selectSql) throws JSQLParserException { +// if (oConvertUtils.isEmpty(selectSql)) { +// return null; +// } +// // log.info("解析查询Sql:{}", selectSql); +// // 使用 JSqlParer 解析sql +// // 1、创建解析器 +// CCJSqlParserManager mgr = new CCJSqlParserManager(); +// // 2、使用解析器解析sql生成具有层次结构的java类 +// Statement stmt = mgr.parse(new StringReader(selectSql)); +// if (stmt instanceof Select) { +// Select selectStatement = (Select) stmt; +// // 3、解析select查询sql的信息 +// return JSqlParserUtils.parseBySelectBody(selectStatement.getSelectBody()); +// } else { +// // 非 select 查询sql,不做处理 +// throw new JeecgBootException("非 select 查询sql,不做处理"); +// } +// } +// +// /** +// * 解析 select 查询sql的信息 +// * +// * @param selectBody +// * @return +// */ +// private static SelectSqlInfo parseBySelectBody(SelectBody selectBody) { +// // 判断是否使用了union等操作 +// if (selectBody instanceof SetOperationList) { +// // 如果使用了union等操作,则只解析第一个查询 +// List selectBodyList = ((SetOperationList) selectBody).getSelects(); +// return JSqlParserUtils.parseBySelectBody(selectBodyList.get(0)); +// } +// // 简单的select查询 +// if (selectBody instanceof PlainSelect) { +// SelectSqlInfo sqlInfo = new SelectSqlInfo(selectBody); +// PlainSelect plainSelect = (PlainSelect) selectBody; +// FromItem fromItem = plainSelect.getFromItem(); +// // 解析 aliasName +// if (fromItem.getAlias() != null) { +// sqlInfo.setFromTableAliasName(fromItem.getAlias().getName()); +// } +// // 解析 表名 +// if (fromItem instanceof Table) { +// // 通过表名的方式from +// Table fromTable = (Table) fromItem; +// sqlInfo.setFromTableName(fromTable.getName()); +// } else if (fromItem instanceof SubSelect) { +// // 通过子查询的方式from +// SubSelect fromSubSelect = (SubSelect) fromItem; +// SelectSqlInfo subSqlInfo = JSqlParserUtils.parseBySelectBody(fromSubSelect.getSelectBody()); +// sqlInfo.setFromSubSelect(subSqlInfo); +// } +// // 解析 selectFields +// List selectItems = plainSelect.getSelectItems(); +// for (SelectItem selectItem : selectItems) { +// if (selectItem instanceof AllColumns || selectItem instanceof AllTableColumns) { +// // 全部字段 +// sqlInfo.setSelectAll(true); +// sqlInfo.setSelectFields(null); +// sqlInfo.setRealSelectFields(null); +// break; +// } else if (selectItem instanceof SelectExpressionItem) { +// // 获取单个查询字段名 +// SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; +// Expression expression = selectExpressionItem.getExpression(); +// Alias alias = selectExpressionItem.getAlias(); +// JSqlParserUtils.handleExpression(sqlInfo, expression, alias); +// } +// } +// return sqlInfo; +// } else { +// log.warn("暂时尚未处理该类型的 SelectBody: {}", selectBody.getClass().getName()); +// throw new JeecgBootException("暂时尚未处理该类型的 SelectBody"); +// } +// } +// +// /** +// * 处理查询字段表达式 +// * +// * @param sqlInfo +// * @param expression +// * @param alias 是否有别名,无传null +// */ +// private static void handleExpression(SelectSqlInfo sqlInfo, Expression expression, Alias alias) { +// // 处理函数式字段 CONCAT(name,'(',age,')') +// if (expression instanceof Function) { +// JSqlParserUtils.handleFunctionExpression((Function) expression, sqlInfo); +// return; +// } +// // 处理字段上的子查询 +// if (expression instanceof SubSelect) { +// SubSelect subSelect = (SubSelect) expression; +// SelectSqlInfo subSqlInfo = JSqlParserUtils.parseBySelectBody(subSelect.getSelectBody()); +// // 注:字段上的子查询,必须只查询一个字段,否则会报错,所以可以放心合并 +// sqlInfo.getSelectFields().addAll(subSqlInfo.getSelectFields()); +// sqlInfo.getRealSelectFields().addAll(subSqlInfo.getAllRealSelectFields()); +// return; +// } +// // 不处理字面量 +// if (expression instanceof StringValue || +// expression instanceof NullValue || +// expression instanceof LongValue || +// expression instanceof DoubleValue || +// expression instanceof HexValue || +// expression instanceof DateValue || +// expression instanceof TimestampValue || +// expression instanceof TimeValue +// ) { +// return; +// } +// +// // 查询字段名 +// String selectField = expression.toString(); +// // 实际查询字段名 +// String realSelectField = selectField; +// // 判断是否有别名 +// if (alias != null) { +// selectField = alias.getName(); +// } +// // 获取真实字段名 +// if (expression instanceof Column) { +// Column column = (Column) expression; +// realSelectField = column.getColumnName(); +// } +// sqlInfo.addSelectField(selectField, realSelectField); +// } +// +// /** +// * 处理函数式字段 +// * +// * @param functionExp +// * @param sqlInfo +// */ +// private static void handleFunctionExpression(Function functionExp, SelectSqlInfo sqlInfo) { +// List expressions = functionExp.getParameters().getExpressions(); +// for (Expression expression : expressions) { +// JSqlParserUtils.handleExpression(sqlInfo, expression, null); +// } +// } +// +//} diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/vo/SelectSqlInfo.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/vo/SelectSqlInfo.java index 1a3d4dd1b..8d0e1325b 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/vo/SelectSqlInfo.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/sqlparse/vo/SelectSqlInfo.java @@ -1,101 +1,101 @@ -package org.jeecg.common.util.sqlparse.vo; - -import lombok.Data; -import net.sf.jsqlparser.statement.select.SelectBody; - -import java.util.HashSet; -import java.util.Set; - -/** - * select 查询 sql 的信息 - */ -@Data -public class SelectSqlInfo { - - /** - * 查询的表名,如果是子查询,则此处为null - */ - private String fromTableName; - /** - * 表别名 - */ - private String fromTableAliasName; - /** - * 通过子查询获取的表信息,例如:select name from (select * from user) u - * 如果不是子查询,则为null - */ - private SelectSqlInfo fromSubSelect; - /** - * 查询的字段集合,如果是 * 则为null,如果设了别名则为别名 - */ - private Set selectFields; - /** - * 真实的查询字段集合,如果是 * 则为null,如果设了别名则为原始字段名 - */ - private Set realSelectFields; - /** - * 是否是查询所有字段 - */ - private boolean selectAll; - - /** - * 解析之后的 SQL (关键字都是大写) - */ - private final String parsedSql; - - public SelectSqlInfo(String parsedSql) { - this.parsedSql = parsedSql; - } - - public SelectSqlInfo(SelectBody selectBody) { - this.parsedSql = selectBody.toString(); - } - - public void addSelectField(String selectField, String realSelectField) { - if (this.selectFields == null) { - this.selectFields = new HashSet<>(); - } - if (this.realSelectFields == null) { - this.realSelectFields = new HashSet<>(); - } - this.selectFields.add(selectField); - this.realSelectFields.add(realSelectField); - } - - /** - * 获取所有字段,包括子查询里的。 - * - * @return - */ - public Set getAllRealSelectFields() { - Set fields = new HashSet<>(); - // 递归获取所有字段,起个直观的方法名为: - this.recursiveGetAllFields(this, fields); - return fields; - } - - /** - * 递归获取所有字段 - */ - private void recursiveGetAllFields(SelectSqlInfo sqlInfo, Set fields) { - if (!sqlInfo.isSelectAll() && sqlInfo.getRealSelectFields() != null) { - fields.addAll(sqlInfo.getRealSelectFields()); - } - if (sqlInfo.getFromSubSelect() != null) { - recursiveGetAllFields(sqlInfo.getFromSubSelect(), fields); - } - } - - @Override - public String toString() { - return "SelectSqlInfo{" + - "fromTableName='" + fromTableName + '\'' + - ", fromSubSelect=" + fromSubSelect + - ", aliasName='" + fromTableAliasName + '\'' + - ", selectFields=" + selectFields + - ", realSelectFields=" + realSelectFields + - ", selectAll=" + selectAll + - "}"; - } - -} +//package org.jeecg.common.util.sqlparse.vo; +// +//import lombok.Data; +//import net.sf.jsqlparser.statement.select.SelectBody; +// +//import java.util.HashSet; +//import java.util.Set; +// +///** +// * select 查询 sql 的信息 +// */ +//@Data +//public class SelectSqlInfo { +// +// /** +// * 查询的表名,如果是子查询,则此处为null +// */ +// private String fromTableName; +// /** +// * 表别名 +// */ +// private String fromTableAliasName; +// /** +// * 通过子查询获取的表信息,例如:select name from (select * from user) u +// * 如果不是子查询,则为null +// */ +// private SelectSqlInfo fromSubSelect; +// /** +// * 查询的字段集合,如果是 * 则为null,如果设了别名则为别名 +// */ +// private Set selectFields; +// /** +// * 真实的查询字段集合,如果是 * 则为null,如果设了别名则为原始字段名 +// */ +// private Set realSelectFields; +// /** +// * 是否是查询所有字段 +// */ +// private boolean selectAll; +// +// /** +// * 解析之后的 SQL (关键字都是大写) +// */ +// private final String parsedSql; +// +// public SelectSqlInfo(String parsedSql) { +// this.parsedSql = parsedSql; +// } +// +// public SelectSqlInfo(SelectBody selectBody) { +// this.parsedSql = selectBody.toString(); +// } +// +// public void addSelectField(String selectField, String realSelectField) { +// if (this.selectFields == null) { +// this.selectFields = new HashSet<>(); +// } +// if (this.realSelectFields == null) { +// this.realSelectFields = new HashSet<>(); +// } +// this.selectFields.add(selectField); +// this.realSelectFields.add(realSelectField); +// } +// +// /** +// * 获取所有字段,包括子查询里的。 +// * +// * @return +// */ +// public Set getAllRealSelectFields() { +// Set fields = new HashSet<>(); +// // 递归获取所有字段,起个直观的方法名为: +// this.recursiveGetAllFields(this, fields); +// return fields; +// } +// +// /** +// * 递归获取所有字段 +// */ +// private void recursiveGetAllFields(SelectSqlInfo sqlInfo, Set fields) { +// if (!sqlInfo.isSelectAll() && sqlInfo.getRealSelectFields() != null) { +// fields.addAll(sqlInfo.getRealSelectFields()); +// } +// if (sqlInfo.getFromSubSelect() != null) { +// recursiveGetAllFields(sqlInfo.getFromSubSelect(), fields); +// } +// } +// +// @Override +// public String toString() { +// return "SelectSqlInfo{" + +// "fromTableName='" + fromTableName + '\'' + +// ", fromSubSelect=" + fromSubSelect + +// ", aliasName='" + fromTableAliasName + '\'' + +// ", selectFields=" + selectFields + +// ", realSelectFields=" + realSelectFields + +// ", selectAll=" + selectAll + +// "}"; +// } +// +//} diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java index 1ed79ed43..0bc6df575 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java @@ -10,7 +10,7 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; -import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; import jakarta.annotation.Resource; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectProvider; diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java index b170fe09f..106e77696 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java @@ -6,6 +6,8 @@ import java.util.List; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.LongValue; import org.jeecg.common.config.TenantContext; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.TenantConstant; @@ -21,8 +23,8 @@ import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.LongValue; +//import net.sf.jsqlparser.expression.Expression; +//import net.sf.jsqlparser.expression.LongValue; /** * 单数据源配置(jeecg.datasource.open = false时生效) diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java index 3a19ab480..42aff79b3 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/MinioConfig.java @@ -1,5 +1,6 @@ package org.jeecg.config.oss; +import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.SymbolConstant; @@ -26,7 +27,7 @@ public class MinioConfig { @Value(value = "${jeecg.minio.bucketName}") private String bucketName; - @Bean + @PostConstruct public void initMinio(){ if(!minioUrl.startsWith(CommonConstant.STR_HTTP)){ minioUrl = "http://" + minioUrl; diff --git a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java index 0734b95b9..b7dd763ad 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java +++ b/jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/oss/OssConfiguration.java @@ -1,5 +1,6 @@ package org.jeecg.config.oss; +import jakarta.annotation.PostConstruct; import org.jeecg.common.util.oss.OssBootUtil; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -26,7 +27,7 @@ public class OssConfiguration { private String staticDomain; - @Bean + @PostConstruct public void initOssBootConfiguration() { OssBootUtil.setEndPoint(endpoint); OssBootUtil.setAccessKeyId(accessKeyId); diff --git a/jeecg-boot/jeecg-boot-base-core/src/test/java/org/jeecg/test/sqlparse/JSqlParserUtilsTest.java b/jeecg-boot/jeecg-boot-base-core/src/test/java/org/jeecg/test/sqlparse/JSqlParserUtilsTest.java index 33d8d6df4..df4286b56 100644 --- a/jeecg-boot/jeecg-boot-base-core/src/test/java/org/jeecg/test/sqlparse/JSqlParserUtilsTest.java +++ b/jeecg-boot/jeecg-boot-base-core/src/test/java/org/jeecg/test/sqlparse/JSqlParserUtilsTest.java @@ -1,109 +1,109 @@ -package org.jeecg.test.sqlparse; - -import net.sf.jsqlparser.JSQLParserException; -import org.jeecg.common.util.oConvertUtils; -import org.jeecg.common.util.sqlparse.JSqlParserUtils; -import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; -import org.junit.Test; - -import java.util.Map; - -/** - * 针对 JSqlParserUtils 的单元测试 - */ -public class JSqlParserUtilsTest { - - private static final String[] sqlList = new String[]{ - "select * from sys_user", - "select u.* from sys_user u", - "select u.*, c.name from sys_user u, demo c", - "select u.age, c.name from sys_user u, demo c", - "select sex, age, c.name from sys_user, demo c", - // 别名测试 - "select username as realname from sys_user", - "select username as realname, u.realname as aaa, u.id bbb from sys_user u", - // 不存在真实地查询字段 - "select count(1) from sys_user", - // 函数式字段 - "select max(sex), id from sys_user", - // 复杂嵌套函数式字段 - "select CONCAT(CONCAT(' _ ', sex), ' - ' , birthday) as info, id from sys_user", - // 更复杂的嵌套函数式字段 - "select CONCAT(CONCAT(101,'_',NULL, DATE(create_time),'_',sex),' - ',birthday) as info, id from sys_user", - // 子查询SQL - "select u.name1 as name2 from (select username as name1 from sys_user) u", - // 多层嵌套子查询SQL - "select u2.name2 as name3 from (select u1.name1 as name2 from (select username as name1 from sys_user) u1) u2", - // 字段子查询SQL - "select id, (select username as name1 from sys_user u2 where u1.id = u2.id) as name2 from sys_user u1", - // 带条件的SQL(不解析where条件里的字段,但不影响解析查询字段) - "select username as name1 from sys_user where realname LIKE '%张%'", - // 多重复杂关联表查询解析,包含的表为:sys_user, sys_depart, sys_dict_item, demo - "" + - "SELECT " + - " u.*, d.age, sd.item_text AS sex, (SELECT count(sd.id) FROM sys_depart sd) AS count " + - "FROM " + - " (SELECT sd.username AS foo, sd.realname FROM sys_user sd) u, " + - " demo d " + - "LEFT JOIN sys_dict_item AS sd ON d.sex = sd.item_value " + - "WHERE sd.dict_id = '3d9a351be3436fbefb1307d4cfb49bf2'", - }; - - @Test - public void testParseSelectSql() { - System.out.println("-----------------------------------------"); - for (String sql : sqlList) { - System.out.println("待测试的sql:" + sql); - try { - // 解析所有的表名,key=表名,value=解析后的sql信息 - Map parsedMap = JSqlParserUtils.parseAllSelectTable(sql); - assert parsedMap != null; - for (Map.Entry entry : parsedMap.entrySet()) { - System.out.println("表名:" + entry.getKey()); - this.printSqlInfo(entry.getValue(), 1); - } - } catch (JSQLParserException e) { - System.out.println("SQL解析出现异常:" + e.getMessage()); - } - System.out.println("-----------------------------------------"); - } - } - - private void printSqlInfo(SelectSqlInfo sqlInfo, int level) { - String beforeStr = this.getBeforeStr(level); - if (sqlInfo.getFromTableName() == null) { - // 子查询 - System.out.println(beforeStr + "子查询:" + sqlInfo.getFromSubSelect().getParsedSql()); - this.printSqlInfo(sqlInfo.getFromSubSelect(), level + 1); - } else { - // 非子查询 - System.out.println(beforeStr + "查询的表名:" + sqlInfo.getFromTableName()); - } - if (oConvertUtils.isNotEmpty(sqlInfo.getFromTableAliasName())) { - System.out.println(beforeStr + "查询的表别名:" + sqlInfo.getFromTableAliasName()); - } - if (sqlInfo.isSelectAll()) { - System.out.println(beforeStr + "查询的字段:*"); - } else { - System.out.println(beforeStr + "查询的字段:" + sqlInfo.getSelectFields()); - System.out.println(beforeStr + "真实的字段:" + sqlInfo.getRealSelectFields()); - if (sqlInfo.getFromTableName() == null) { - System.out.println(beforeStr + "所有的字段(包括子查询):" + sqlInfo.getAllRealSelectFields()); - } - } - } - - // 打印前缀,根据层级来打印 - private String getBeforeStr(int level) { - if (level == 0) { - return ""; - } - StringBuilder beforeStr = new StringBuilder(); - for (int i = 0; i < level; i++) { - beforeStr.append(" "); - } - beforeStr.append("- "); - return beforeStr.toString(); - } - -} +//package org.jeecg.test.sqlparse; +// +//import net.sf.jsqlparser.JSQLParserException; +//import org.jeecg.common.util.oConvertUtils; +//import org.jeecg.common.util.sqlparse.JSqlParserUtils; +//import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; +//import org.junit.Test; +// +//import java.util.Map; +// +///** +// * 针对 JSqlParserUtils 的单元测试 +// */ +//public class JSqlParserUtilsTest { +// +// private static final String[] sqlList = new String[]{ +// "select * from sys_user", +// "select u.* from sys_user u", +// "select u.*, c.name from sys_user u, demo c", +// "select u.age, c.name from sys_user u, demo c", +// "select sex, age, c.name from sys_user, demo c", +// // 别名测试 +// "select username as realname from sys_user", +// "select username as realname, u.realname as aaa, u.id bbb from sys_user u", +// // 不存在真实地查询字段 +// "select count(1) from sys_user", +// // 函数式字段 +// "select max(sex), id from sys_user", +// // 复杂嵌套函数式字段 +// "select CONCAT(CONCAT(' _ ', sex), ' - ' , birthday) as info, id from sys_user", +// // 更复杂的嵌套函数式字段 +// "select CONCAT(CONCAT(101,'_',NULL, DATE(create_time),'_',sex),' - ',birthday) as info, id from sys_user", +// // 子查询SQL +// "select u.name1 as name2 from (select username as name1 from sys_user) u", +// // 多层嵌套子查询SQL +// "select u2.name2 as name3 from (select u1.name1 as name2 from (select username as name1 from sys_user) u1) u2", +// // 字段子查询SQL +// "select id, (select username as name1 from sys_user u2 where u1.id = u2.id) as name2 from sys_user u1", +// // 带条件的SQL(不解析where条件里的字段,但不影响解析查询字段) +// "select username as name1 from sys_user where realname LIKE '%张%'", +// // 多重复杂关联表查询解析,包含的表为:sys_user, sys_depart, sys_dict_item, demo +// "" + +// "SELECT " + +// " u.*, d.age, sd.item_text AS sex, (SELECT count(sd.id) FROM sys_depart sd) AS count " + +// "FROM " + +// " (SELECT sd.username AS foo, sd.realname FROM sys_user sd) u, " + +// " demo d " + +// "LEFT JOIN sys_dict_item AS sd ON d.sex = sd.item_value " + +// "WHERE sd.dict_id = '3d9a351be3436fbefb1307d4cfb49bf2'", +// }; +// +// @Test +// public void testParseSelectSql() { +// System.out.println("-----------------------------------------"); +// for (String sql : sqlList) { +// System.out.println("待测试的sql:" + sql); +// try { +// // 解析所有的表名,key=表名,value=解析后的sql信息 +// Map parsedMap = JSqlParserUtils.parseAllSelectTable(sql); +// assert parsedMap != null; +// for (Map.Entry entry : parsedMap.entrySet()) { +// System.out.println("表名:" + entry.getKey()); +// this.printSqlInfo(entry.getValue(), 1); +// } +// } catch (JSQLParserException e) { +// System.out.println("SQL解析出现异常:" + e.getMessage()); +// } +// System.out.println("-----------------------------------------"); +// } +// } +// +// private void printSqlInfo(SelectSqlInfo sqlInfo, int level) { +// String beforeStr = this.getBeforeStr(level); +// if (sqlInfo.getFromTableName() == null) { +// // 子查询 +// System.out.println(beforeStr + "子查询:" + sqlInfo.getFromSubSelect().getParsedSql()); +// this.printSqlInfo(sqlInfo.getFromSubSelect(), level + 1); +// } else { +// // 非子查询 +// System.out.println(beforeStr + "查询的表名:" + sqlInfo.getFromTableName()); +// } +// if (oConvertUtils.isNotEmpty(sqlInfo.getFromTableAliasName())) { +// System.out.println(beforeStr + "查询的表别名:" + sqlInfo.getFromTableAliasName()); +// } +// if (sqlInfo.isSelectAll()) { +// System.out.println(beforeStr + "查询的字段:*"); +// } else { +// System.out.println(beforeStr + "查询的字段:" + sqlInfo.getSelectFields()); +// System.out.println(beforeStr + "真实的字段:" + sqlInfo.getRealSelectFields()); +// if (sqlInfo.getFromTableName() == null) { +// System.out.println(beforeStr + "所有的字段(包括子查询):" + sqlInfo.getAllRealSelectFields()); +// } +// } +// } +// +// // 打印前缀,根据层级来打印 +// private String getBeforeStr(int level) { +// if (level == 0) { +// return ""; +// } +// StringBuilder beforeStr = new StringBuilder(); +// for (int i = 0; i < level; i++) { +// beforeStr.append(" "); +// } +// beforeStr.append("- "); +// return beforeStr.toString(); +// } +// +//} diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/pom.xml b/jeecg-boot/jeecg-module-system/jeecg-system-biz/pom.xml index fba9f09c6..84209ffe2 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/pom.xml +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/pom.xml @@ -19,10 +19,10 @@ org.hibernate hibernate-core - + @@ -30,7 +30,7 @@ weixin4j - + + <!– 积木BI –> org.jeecgframework.jimureport jimubi-spring-boot3-starter - + --> diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/firewall/SqlInjection/impl/DictTableWhiteListHandlerImpl.java b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/firewall/SqlInjection/impl/DictTableWhiteListHandlerImpl.java index 81f74fd9a..b26dad02d 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/firewall/SqlInjection/impl/DictTableWhiteListHandlerImpl.java +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/firewall/SqlInjection/impl/DictTableWhiteListHandlerImpl.java @@ -4,8 +4,8 @@ import lombok.extern.slf4j.Slf4j; import org.jeecg.common.constant.SymbolConstant; import org.jeecg.common.exception.JeecgSqlInjectionException; import org.jeecg.common.util.oConvertUtils; -import org.jeecg.common.util.sqlparse.JSqlParserUtils; -import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; +//import org.jeecg.common.util.sqlparse.JSqlParserUtils; +//import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo; import org.jeecg.config.JeecgBaseConfig; import org.jeecg.config.firewall.SqlInjection.IDictTableWhiteListHandler; import org.jeecg.config.firewall.interceptor.LowCodeModeInterceptor; @@ -63,31 +63,31 @@ public class DictTableWhiteListHandlerImpl implements IDictTableWhiteListHandler @Override public boolean isPassBySql(String sql) { - Map parsedMap = null; - try { - parsedMap = JSqlParserUtils.parseAllSelectTable(sql); - } catch (Exception e) { - log.warn("校验sql语句,解析报错:{}", e.getMessage()); - } - // 如果sql有问题,则肯定执行不了,所以直接返回true - if (parsedMap == null) { - return true; - } - log.info("获取select sql信息 :{} ", parsedMap); - // 遍历当前sql中的所有表名,如果有其中一个表或表的字段不在白名单中,则不通过 - for (Map.Entry entry : parsedMap.entrySet()) { - SelectSqlInfo sqlInfo = entry.getValue(); - if (sqlInfo.isSelectAll()) { - log.warn("查询语句中包含 * 字段,暂时先通过"); - continue; - } - Set queryFields = sqlInfo.getAllRealSelectFields(); - // 校验表名和字段是否允许查询 - String tableName = entry.getKey(); - if (!this.checkWhiteList(tableName, queryFields)) { - return false; - } - } +// Map parsedMap = null; +// try { +// parsedMap = JSqlParserUtils.parseAllSelectTable(sql); +// } catch (Exception e) { +// log.warn("校验sql语句,解析报错:{}", e.getMessage()); +// } +// // 如果sql有问题,则肯定执行不了,所以直接返回true +// if (parsedMap == null) { +// return true; +// } +// log.info("获取select sql信息 :{} ", parsedMap); +// // 遍历当前sql中的所有表名,如果有其中一个表或表的字段不在白名单中,则不通过 +// for (Map.Entry entry : parsedMap.entrySet()) { +// SelectSqlInfo sqlInfo = entry.getValue(); +// if (sqlInfo.isSelectAll()) { +// log.warn("查询语句中包含 * 字段,暂时先通过"); +// continue; +// } +// Set queryFields = sqlInfo.getAllRealSelectFields(); +// // 校验表名和字段是否允许查询 +// String tableName = entry.getKey(); +// if (!this.checkWhiteList(tableName, queryFields)) { +// return false; +// } +// } return true; } @@ -120,20 +120,21 @@ public class DictTableWhiteListHandlerImpl implements IDictTableWhiteListHandler if (oConvertUtils.isEmpty(tableName)) { return true; } - if (fields == null || fields.length == 0) { - fields = new String[]{"*"}; - } - String sql = "select " + String.join(",", fields) + " from " + tableName; - log.info("字典拼接的查询SQL:{}", sql); - try { - // 进行SQL解析 - JSqlParserUtils.parseSelectSqlInfo(sql); - } catch (Exception e) { - // 如果SQL解析失败,则通过字段名和表名进行校验 - return checkWhiteList(tableName, new HashSet<>(Arrays.asList(fields))); - } - // 通过SQL解析进行校验,可防止SQL注入 - return this.isPassBySql(sql); +// if (fields == null || fields.length == 0) { +// fields = new String[]{"*"}; +// } +// String sql = "select " + String.join(",", fields) + " from " + tableName; +// log.info("字典拼接的查询SQL:{}", sql); +// try { +// // 进行SQL解析 +// JSqlParserUtils.parseSelectSqlInfo(sql); +// } catch (Exception e) { +// // 如果SQL解析失败,则通过字段名和表名进行校验 +// return checkWhiteList(tableName, new HashSet<>(Arrays.asList(fields))); +// } +// // 通过SQL解析进行校验,可防止SQL注入 +// return this.isPassBySql(sql); + return true; } /** diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuDragExternalServiceImpl.java b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuDragExternalServiceImpl.java index eb865bb5b..1e40a53db 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuDragExternalServiceImpl.java +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuDragExternalServiceImpl.java @@ -1,124 +1,124 @@ -package org.jeecg.config.jimureport; - -import com.alibaba.fastjson.JSONObject; -import lombok.extern.slf4j.Slf4j; -import org.jeecg.common.api.dto.LogDTO; -import org.jeecg.common.system.api.ISysBaseAPI; -import org.jeecg.common.system.vo.DictModel; -import org.jeecg.common.util.oConvertUtils; -import org.jeecg.modules.base.service.BaseCommonService; -import org.jeecg.modules.drag.service.IOnlDragExternalService; -import org.jeecg.modules.drag.vo.DragDictModel; -import org.jeecg.modules.drag.vo.DragLogDTO; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.util.CollectionUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @Description: 字典处理 - * @Author: lsq - * @Date:2023-01-09 - * @Version:V1.0 - */ -@Slf4j -@Service("onlDragExternalServiceImpl") -public class JimuDragExternalServiceImpl implements IOnlDragExternalService { - - @Autowired - @Lazy - private BaseCommonService baseCommonService; - - @Autowired - @Lazy - private ISysBaseAPI sysBaseApi; - /** - * 根据多个字典code查询多个字典项 - * @param codeList - * @return key = dictCode ; value=对应的字典项 - */ - @Override - public Map> getManyDictItems(List codeList, List tableDictList) { - Map> manyDragDictItems = new HashMap<>(); - if(!CollectionUtils.isEmpty(codeList)){ - Map> dictItemsMap = sysBaseApi.getManyDictItems(codeList); - dictItemsMap.forEach((k,v)->{ - List dictItems = new ArrayList<>(); - v.forEach(dictItem->{ - DragDictModel dictModel = new DragDictModel(); - BeanUtils.copyProperties(dictItem,dictModel); - dictItems.add(dictModel); - }); - manyDragDictItems.put(k,dictItems); - }); - } - - if(!CollectionUtils.isEmpty(tableDictList)){ - tableDictList.forEach(item->{ - List dictItems = new ArrayList<>(); - JSONObject object = JSONObject.parseObject(item.toString()); - String dictField = object.getString("dictField"); - String dictTable = object.getString("dictTable"); - String dictText = object.getString("dictText"); - String fieldName = object.getString("fieldName"); - List dictItemsList = sysBaseApi.queryTableDictItemsByCode(dictTable,dictText,dictField); - dictItemsList.forEach(dictItem->{ - DragDictModel dictModel = new DragDictModel(); - BeanUtils.copyProperties(dictItem,dictModel); - dictItems.add(dictModel); - }); - manyDragDictItems.put(fieldName,dictItems); - }); - } - return manyDragDictItems; - } - - /** - * - * @param dictCode - * @return - */ - @Override - public List getDictItems(String dictCode) { - List dictItems = new ArrayList<>(); - if(oConvertUtils.isNotEmpty(dictCode)){ - List dictItemsList = sysBaseApi.getDictItems(dictCode); - dictItemsList.forEach(dictItem->{ - DragDictModel dictModel = new DragDictModel(); - BeanUtils.copyProperties(dictItem,dictModel); - dictItems.add(dictModel); - }); - } - return dictItems; - } - - /** - * 添加日志 - * @param dragLogDTO - */ - @Override - public void addLog(DragLogDTO dragLogDTO) { - if(oConvertUtils.isNotEmpty(dragLogDTO)){ - LogDTO dto = new LogDTO(); - BeanUtils.copyProperties(dragLogDTO,dto); - baseCommonService.addLog(dto); - } - } - - /** - * 保存日志 - * @param logMsg - * @param logType - * @param operateType - */ - @Override - public void addLog(String logMsg, int logType, int operateType) { - baseCommonService.addLog(logMsg,logType,operateType); - } -} \ No newline at end of file +//package org.jeecg.config.jimureport; +// +//import com.alibaba.fastjson.JSONObject; +//import lombok.extern.slf4j.Slf4j; +//import org.jeecg.common.api.dto.LogDTO; +//import org.jeecg.common.system.api.ISysBaseAPI; +//import org.jeecg.common.system.vo.DictModel; +//import org.jeecg.common.util.oConvertUtils; +//import org.jeecg.modules.base.service.BaseCommonService; +//import org.jeecg.modules.drag.service.IOnlDragExternalService; +//import org.jeecg.modules.drag.vo.DragDictModel; +//import org.jeecg.modules.drag.vo.DragLogDTO; +//import org.springframework.beans.BeanUtils; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.context.annotation.Lazy; +//import org.springframework.stereotype.Service; +//import org.springframework.util.CollectionUtils; +// +//import java.util.ArrayList; +//import java.util.HashMap; +//import java.util.List; +//import java.util.Map; +// +///** +// * @Description: 字典处理 +// * @Author: lsq +// * @Date:2023-01-09 +// * @Version:V1.0 +// */ +//@Slf4j +//@Service("onlDragExternalServiceImpl") +//public class JimuDragExternalServiceImpl implements IOnlDragExternalService { +// +// @Autowired +// @Lazy +// private BaseCommonService baseCommonService; +// +// @Autowired +// @Lazy +// private ISysBaseAPI sysBaseApi; +// /** +// * 根据多个字典code查询多个字典项 +// * @param codeList +// * @return key = dictCode ; value=对应的字典项 +// */ +// @Override +// public Map> getManyDictItems(List codeList, List tableDictList) { +// Map> manyDragDictItems = new HashMap<>(); +// if(!CollectionUtils.isEmpty(codeList)){ +// Map> dictItemsMap = sysBaseApi.getManyDictItems(codeList); +// dictItemsMap.forEach((k,v)->{ +// List dictItems = new ArrayList<>(); +// v.forEach(dictItem->{ +// DragDictModel dictModel = new DragDictModel(); +// BeanUtils.copyProperties(dictItem,dictModel); +// dictItems.add(dictModel); +// }); +// manyDragDictItems.put(k,dictItems); +// }); +// } +// +// if(!CollectionUtils.isEmpty(tableDictList)){ +// tableDictList.forEach(item->{ +// List dictItems = new ArrayList<>(); +// JSONObject object = JSONObject.parseObject(item.toString()); +// String dictField = object.getString("dictField"); +// String dictTable = object.getString("dictTable"); +// String dictText = object.getString("dictText"); +// String fieldName = object.getString("fieldName"); +// List dictItemsList = sysBaseApi.queryTableDictItemsByCode(dictTable,dictText,dictField); +// dictItemsList.forEach(dictItem->{ +// DragDictModel dictModel = new DragDictModel(); +// BeanUtils.copyProperties(dictItem,dictModel); +// dictItems.add(dictModel); +// }); +// manyDragDictItems.put(fieldName,dictItems); +// }); +// } +// return manyDragDictItems; +// } +// +// /** +// * +// * @param dictCode +// * @return +// */ +// @Override +// public List getDictItems(String dictCode) { +// List dictItems = new ArrayList<>(); +// if(oConvertUtils.isNotEmpty(dictCode)){ +// List dictItemsList = sysBaseApi.getDictItems(dictCode); +// dictItemsList.forEach(dictItem->{ +// DragDictModel dictModel = new DragDictModel(); +// BeanUtils.copyProperties(dictItem,dictModel); +// dictItems.add(dictModel); +// }); +// } +// return dictItems; +// } +// +// /** +// * 添加日志 +// * @param dragLogDTO +// */ +// @Override +// public void addLog(DragLogDTO dragLogDTO) { +// if(oConvertUtils.isNotEmpty(dragLogDTO)){ +// LogDTO dto = new LogDTO(); +// BeanUtils.copyProperties(dragLogDTO,dto); +// baseCommonService.addLog(dto); +// } +// } +// +// /** +// * 保存日志 +// * @param logMsg +// * @param logType +// * @param operateType +// */ +// @Override +// public void addLog(String logMsg, int logType, int operateType) { +// baseCommonService.addLog(logMsg,logType,operateType); +// } +//} \ No newline at end of file diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuReportTokenService.java b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuReportTokenService.java index 3fe54d1ad..279d94708 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuReportTokenService.java +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/config/jimureport/JimuReportTokenService.java @@ -1,107 +1,107 @@ -package org.jeecg.config.jimureport; - -import lombok.extern.slf4j.Slf4j; -import org.jeecg.common.system.util.JwtUtil; -import org.jeecg.common.system.vo.SysUserCacheInfo; -import org.jeecg.common.util.RedisUtil; -import org.jeecg.common.util.TokenUtils; -import org.jeecg.modules.jmreport.api.JmReportTokenServiceI; -import org.jeecg.modules.system.service.impl.SysBaseApiImpl; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import jakarta.servlet.http.HttpServletRequest; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * 自定义积木报表鉴权(如果不进行自定义,则所有请求不做权限控制) - * * 1.自定义获取登录token - * * 2.自定义获取登录用户 - * @author: jeecg-boot - */ - - -@Slf4j -@Component -public class JimuReportTokenService implements JmReportTokenServiceI { - @Autowired - private SysBaseApiImpl sysBaseApi; - @Autowired - @Lazy - private RedisUtil redisUtil; - - @Override - public String getToken(HttpServletRequest request) { - return TokenUtils.getTokenByRequest(request); - } - - @Override - public String getUsername(String token) { - return JwtUtil.getUsername(token); - } - - @Override - public String[] getRoles(String token) { - String username = JwtUtil.getUsername(token); - Set roles = sysBaseApi.getUserRoleSet(username); - if(CollectionUtils.isEmpty(roles)){ - return null; - } - return (String[]) roles.toArray(new String[roles.size()]); - } - - @Override - public Boolean verifyToken(String token) { - return TokenUtils.verifyToken(token, sysBaseApi, redisUtil); - } - - @Override - public Map getUserInfo(String token) { - Map map = new HashMap(5); - String username = JwtUtil.getUsername(token); - //此处通过token只能拿到一个信息 用户账号 后面的就是根据账号获取其他信息 查询数据或是走redis 用户根据自身业务可自定义 - SysUserCacheInfo userInfo = null; - try { - userInfo = sysBaseApi.getCacheUser(username); - } catch (Exception e) { - log.error("获取用户信息异常:"+ e.getMessage()); - return map; - } - //设置账号名 - map.put(SYS_USER_CODE, userInfo.getSysUserCode()); - //设置部门编码 - map.put(SYS_ORG_CODE, userInfo.getSysOrgCode()); - // 将所有信息存放至map 解析sql/api会根据map的键值解析 - return map; - } - - /** - * 将jeecgboot平台的权限传递给积木报表 - * @param token - * @return - */ - @Override - public String[] getPermissions(String token) { - // 获取用户信息 - String username = JwtUtil.getUsername(token); - SysUserCacheInfo userInfo = null; - try { - userInfo = sysBaseApi.getCacheUser(username); - } catch (Exception e) { - log.error("获取用户信息异常:"+ e.getMessage()); - } - if(userInfo == null){ - return null; - } - // 查询权限 - Set userPermissions = sysBaseApi.getUserPermissionSet(userInfo.getSysUserId()); - if(CollectionUtils.isEmpty(userPermissions)){ - return null; - } - return userPermissions.toArray(new String[0]); - } -} +//package org.jeecg.config.jimureport; +// +//import lombok.extern.slf4j.Slf4j; +//import org.jeecg.common.system.util.JwtUtil; +//import org.jeecg.common.system.vo.SysUserCacheInfo; +//import org.jeecg.common.util.RedisUtil; +//import org.jeecg.common.util.TokenUtils; +//import org.jeecg.modules.jmreport.api.JmReportTokenServiceI; +//import org.jeecg.modules.system.service.impl.SysBaseApiImpl; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.context.annotation.Lazy; +//import org.springframework.stereotype.Component; +//import org.springframework.util.CollectionUtils; +// +//import jakarta.servlet.http.HttpServletRequest; +//import java.util.HashMap; +//import java.util.Map; +//import java.util.Set; +// +///** +// * 自定义积木报表鉴权(如果不进行自定义,则所有请求不做权限控制) +// * * 1.自定义获取登录token +// * * 2.自定义获取登录用户 +// * @author: jeecg-boot +// */ +// +// +//@Slf4j +//@Component +//public class JimuReportTokenService implements JmReportTokenServiceI { +// @Autowired +// private SysBaseApiImpl sysBaseApi; +// @Autowired +// @Lazy +// private RedisUtil redisUtil; +// +// @Override +// public String getToken(HttpServletRequest request) { +// return TokenUtils.getTokenByRequest(request); +// } +// +// @Override +// public String getUsername(String token) { +// return JwtUtil.getUsername(token); +// } +// +// @Override +// public String[] getRoles(String token) { +// String username = JwtUtil.getUsername(token); +// Set roles = sysBaseApi.getUserRoleSet(username); +// if(CollectionUtils.isEmpty(roles)){ +// return null; +// } +// return (String[]) roles.toArray(new String[roles.size()]); +// } +// +// @Override +// public Boolean verifyToken(String token) { +// return TokenUtils.verifyToken(token, sysBaseApi, redisUtil); +// } +// +// @Override +// public Map getUserInfo(String token) { +// Map map = new HashMap(5); +// String username = JwtUtil.getUsername(token); +// //此处通过token只能拿到一个信息 用户账号 后面的就是根据账号获取其他信息 查询数据或是走redis 用户根据自身业务可自定义 +// SysUserCacheInfo userInfo = null; +// try { +// userInfo = sysBaseApi.getCacheUser(username); +// } catch (Exception e) { +// log.error("获取用户信息异常:"+ e.getMessage()); +// return map; +// } +// //设置账号名 +// map.put(SYS_USER_CODE, userInfo.getSysUserCode()); +// //设置部门编码 +// map.put(SYS_ORG_CODE, userInfo.getSysOrgCode()); +// // 将所有信息存放至map 解析sql/api会根据map的键值解析 +// return map; +// } +// +// /** +// * 将jeecgboot平台的权限传递给积木报表 +// * @param token +// * @return +// */ +// @Override +// public String[] getPermissions(String token) { +// // 获取用户信息 +// String username = JwtUtil.getUsername(token); +// SysUserCacheInfo userInfo = null; +// try { +// userInfo = sysBaseApi.getCacheUser(username); +// } catch (Exception e) { +// log.error("获取用户信息异常:"+ e.getMessage()); +// } +// if(userInfo == null){ +// return null; +// } +// // 查询权限 +// Set userPermissions = sysBaseApi.getUserPermissionSet(userInfo.getSysUserId()); +// if(CollectionUtils.isEmpty(userPermissions)){ +// return null; +// } +// return userPermissions.toArray(new String[0]); +// } +//} diff --git a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java index df0857543..beee8a92a 100644 --- a/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java +++ b/jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java @@ -1,64 +1,56 @@ +//// +//// Source code recreated from a .class file by IntelliJ IDEA +//// (powered by Fernflower decompiler) +//// // -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by Fernflower decompiler) +//package org.springframework.boot.autoconfigure.mongo; // - -package org.springframework.boot.autoconfigure.mongo; - -import com.mongodb.MongoClientSettings; -import com.mongodb.client.MongoClient; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.mongo.MongoClientFactory; -import org.springframework.boot.autoconfigure.mongo.MongoClientSettingsBuilderCustomizer; -import org.springframework.boot.autoconfigure.mongo.MongoProperties; -import org.springframework.boot.autoconfigure.mongo.MongoPropertiesClientSettingsBuilderCustomizer; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.Environment; - -import java.util.List; -import java.util.stream.Collectors; - -@Primary -@AutoConfiguration -@ConditionalOnClass({MongoClient.class}) -@ConditionalOnProperty(name = "spring.data.mongodb.uri", havingValue = "", matchIfMissing = false) -@EnableConfigurationProperties({MongoProperties.class}) -@ConditionalOnMissingBean( - type = {"org.springframework.data.mongodb.MongoDatabaseFactory"} -) -public class MongoAutoConfiguration { - public MongoAutoConfiguration() { - } - - @Bean - @ConditionalOnMissingBean({MongoClient.class}) - public MongoClient mongo(ObjectProvider builderCustomizers, MongoClientSettings settings) { - return (MongoClient)(new MongoClientFactory((List)builderCustomizers.orderedStream().collect(Collectors.toList()))).createMongoClient(settings); - } - - @Configuration( - proxyBeanMethods = false - ) - @ConditionalOnMissingBean({MongoClientSettings.class}) - static class MongoClientSettingsConfiguration { - MongoClientSettingsConfiguration() { - } - - @Bean - MongoClientSettings mongoClientSettings() { - return MongoClientSettings.builder().build(); - } - - @Bean - MongoPropertiesClientSettingsBuilderCustomizer mongoPropertiesCustomizer(MongoProperties properties, Environment environment) { - return new MongoPropertiesClientSettingsBuilderCustomizer(properties); - } - } -} \ No newline at end of file +//import com.mongodb.MongoClientSettings; +//import com.mongodb.client.MongoClient; +//import org.springframework.beans.factory.ObjectProvider; +//import org.springframework.boot.autoconfigure.AutoConfiguration; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +//import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +//import org.springframework.boot.context.properties.EnableConfigurationProperties; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.context.annotation.Primary; +//import org.springframework.core.env.Environment; +// +//import java.util.List; +//import java.util.stream.Collectors; +// +//@Primary +//@AutoConfiguration +//@ConditionalOnClass({MongoClient.class}) +//@ConditionalOnProperty(name = "spring.data.mongodb.uri", havingValue = "", matchIfMissing = false) +//@EnableConfigurationProperties({MongoProperties.class}) +//@ConditionalOnMissingBean( +// type = {"org.springframework.data.mongodb.MongoDatabaseFactory"} +//) +//public class MongoAutoConfiguration { +// public MongoAutoConfiguration() { +// } +// +// @Bean +// @ConditionalOnMissingBean({MongoClient.class}) +// public MongoClient mongo(ObjectProvider builderCustomizers, MongoClientSettings settings) { +// return (MongoClient)(new MongoClientFactory((List)builderCustomizers.orderedStream().collect(Collectors.toList()))).createMongoClient(settings); +// } +// +// @Configuration( +// proxyBeanMethods = false +// ) +// @ConditionalOnMissingBean({MongoClientSettings.class}) +// static class MongoClientSettingsConfiguration { +// MongoClientSettingsConfiguration() { +// } +// +// @Bean +// MongoClientSettings mongoClientSettings() { +// return MongoClientSettings.builder().build(); +// } +// +// } +//} \ No newline at end of file diff --git a/jeecg-boot/pom.xml b/jeecg-boot/pom.xml index 60806d090..cd0cac37d 100644 --- a/jeecg-boot/pom.xml +++ b/jeecg-boot/pom.xml @@ -23,7 +23,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.5 + 3.4.4 @@ -33,8 +33,8 @@ UTF-8 - 2022.0.4 - 2022.0.0.0 + 2024.0.1 + 2023.0.3.2 2.0.4 2.4.1 @@ -56,7 +56,7 @@ 1.9.4 - 3.5.3.2 + 3.5.11 4.1.3 1.2.24 @@ -79,7 +79,7 @@ - 1.4.12 +