SQL阻断器
/*
* Copyright (c) 2011-2022, baomidou (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.baomidou.mybatisplus.extension.plugins.inner;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.IsNullExpression;
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import java.sql.Connection;
/**
* 攻击 SQL 阻断解析器,防止全表更新与删除
* extends JsqlParserSupport 得到 SQL 解析功能
* 对 processDelete ,processUpdate 进行重新,进行条件判断操作
* @author hubin
* @since 3.4.0
*/
public class BlockAttackInnerInterceptor extends JsqlParserSupport implements InnerInterceptor {
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
// 将 ibatis StatementHandler 封装成 MPStatementHandler
PluginUtils.MPStatementHandler handler = PluginUtils.mpStatementHandler(sh);
//获取映射语句
MappedStatement ms = handler.mappedStatement();
//得到sql命令类型
SqlCommandType sct = ms.getSqlCommandType();
//只操作 update 和 delete 语句
if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
//判断是否是忽略拦截器的SQL
if (InterceptorIgnoreHelper.willIgnoreBlockAttack(ms.getId())) return;
//得到原生SQL
BoundSql boundSql = handler.boundSql();
//解析多条SQL
//此处不接受返回值,也就是说不依赖处理结果,仅仅只是做判断功能
//实质是:
// 1:super.parserMulti 遍历执行 parserSingle ,
// 2:super.parserSingle 通过 CCJSqlParserUtil.parse(sql); 将SQL解析成 net.sf.jsqlparser.parser.Statement (包含了SQL的属性)
// 3:super.parserSingle 调用 super.processParser 并根据SQL类型触发processDelete,processUpdate
// (为什么不触发查询和新增,因为上面方法上做了判断啊,宝)
parserMulti(boundSql.getSql(), null);
}
}
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
//删除过程处理
this.checkWhere(delete.getTable().getName(), delete.getWhere(), "Prohibition of full table deletion");
}
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
//修改过程处理
this.checkWhere(update.getTable().getName(), update.getWhere(), "Prohibition of table update operation");
}
protected void checkWhere(String tableName, Expression where, String ex) {
// Assert.isFalse:断言 fullMatch 结果一定时false ,否则抛出异常
// getTableLogicField : 得到当前表的逻辑删除字段
Assert.isFalse(this.fullMatch(where, this.getTableLogicField(tableName)), ex);
}
private boolean fullMatch(Expression where, String logicField) {
if (where == null) {
//没有条件 不处理,返回 true ,与断言false 违背 ,抛出异常
return true;
}
if (StringUtils.isNotBlank(logicField)) {
//有逻辑删除字段 假如: logicField == is_delete
//
if (where instanceof BinaryExpression) {
BinaryExpression binaryExpression = (BinaryExpression) where;
// example : is_delete not xxx 或者 is_delete like xxx 等。。
// 禁止使用 逻辑删除字段 做此类运算
if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField) ||
StringUtils.equals(binaryExpression.getRightExpression().toString(), logicField)
) {
return true;
}
}
if (where instanceof IsNullExpression) {
IsNullExpression binaryExpression = (IsNullExpression) where;
// example : is_delete is not null 或者 is_delete isnull
// 禁止使用 逻辑删除字段 做 is not null 或者 isnull 运算
// 如果成立则 与断言false 违背 ,抛出异常
if (StringUtils.equals(binaryExpression.getLeftExpression().toString(), logicField)) {
return true;
}
}
}
//无逻辑删除字段
if (where instanceof EqualsTo) {
// example: 1=1
EqualsTo equalsTo = (EqualsTo) where;
//如示例 : 在 = 的情况下,左右两边条件恒成立, 与断言false 违背 ,抛出异常
return StringUtils.equals(equalsTo.getLeftExpression().toString(), equalsTo.getRightExpression().toString());
} else if (where instanceof NotEqualsTo) {
// example: 1 != 2
NotEqualsTo notEqualsTo = (NotEqualsTo) where;
//如示例 : 在 1= 的情况下,左右两边条件恒不成立, ,与断言false 违背 ,抛出异常
return !StringUtils.equals(notEqualsTo.getLeftExpression().toString(), notEqualsTo.getRightExpression().toString());
} else if (where instanceof OrExpression) {
OrExpression orExpression = (OrExpression) where;
// 在 or 的情况下 不可直接判断, 1=2 or 2=2
// 将 or 左右两边条件拆分后,再次进行 fullMatch 运算
// 如有 一边成立 即条件成立
return fullMatch(orExpression.getLeftExpression(), logicField) || fullMatch(orExpression.getRightExpression(), logicField);
} else if (where instanceof AndExpression) {
// 在 and 的情况下 不可直接判断 1=1 and 2=2
// 将 or 左右两边条件拆分后,再次进行 fullMatch 运算
// 如有 两边都成立 即条件成立
AndExpression andExpression = (AndExpression) where;
return fullMatch(andExpression.getLeftExpression(), logicField) && fullMatch(andExpression.getRightExpression(), logicField);
} else if (where instanceof Parenthesis) {
// example: (1 = 1)
// 如示例 遇到()条件,得到内部表达式 1 = 1(EqualsTo) 进行 fullMatch
Parenthesis parenthesis = (Parenthesis) where;
return fullMatch(parenthesis.getExpression(), logicField);
}
//以上条件都不满足,返回false,与断言一致,不抛出异常
return false;
}
/**
* 获取表名中的逻辑删除字段
*
* @param tableName 表名
* @return 逻辑删除字段
*/
private String getTableLogicField(String tableName) {
//如方法注释 不详解
if (StringUtils.isBlank(tableName)) {
return StringPool.EMPTY;
}
TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
if (tableInfo == null || !tableInfo.isWithLogicDelete() || tableInfo.getLogicDeleteFieldInfo() == null) {
return StringPool.EMPTY;
}
return tableInfo.getLogicDeleteFieldInfo().getColumn();
}
}
上次更新: 2024/11/05, 08:29:31