diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java new file mode 100644 index 0000000..e18e6aa --- /dev/null +++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/SymbolConstant.java @@ -0,0 +1,119 @@ +package org.jeecg.common.constant; + +/** + * @Description: 符号和特殊符号常用类 + * @author: wangshuai + * @date: 2022年03月30日 17:44 + */ +public class SymbolConstant { + + /** + * 符号:点 + */ + public static final String SPOT = "."; + + /** + * 符号:双斜杠 + */ + public static final String DOUBLE_BACKSLASH = "\\"; + + /** + * 符号:冒号 + */ + public static final String COLON = ":"; + + /** + * 符号:逗号 + */ + public static final String COMMA = ","; + + /** + * 符号:左花括号 } + */ + public static final String LEFT_CURLY_BRACKET = "{"; + + /** + * 符号:右花括号 } + */ + public static final String RIGHT_CURLY_BRACKET = "}"; + + /** + * 符号:井号 # + */ + public static final String WELL_NUMBER = "#"; + + /** + * 符号:单斜杠 + */ + public static final String SINGLE_SLASH = "/"; + + /** + * 符号:双斜杠 + */ + public static final String DOUBLE_SLASH = "//"; + + /** + * 符号:感叹号 + */ + public static final String EXCLAMATORY_MARK = "!"; + + /** + * 符号:下划线 + */ + public static final String UNDERLINE = "_"; + + /** + * 符号:单引号 + */ + public static final String SINGLE_QUOTATION_MARK = "'"; + + /** + * 符号:星号 + */ + public static final String ASTERISK = "*"; + + /** + * 符号:百分号 + */ + public static final String PERCENT_SIGN = "%"; + + /** + * 符号:美元 $ + */ + public static final String DOLLAR = "$"; + + /** + * 符号:和 & + */ + public static final String AND = "&"; + + /** + * 符号:../ + */ + public static final String SPOT_SINGLE_SLASH = "../"; + + /** + * 符号:..\\ + */ + public static final String SPOT_DOUBLE_BACKSLASH = "..\\"; + + /** + * 系统变量前缀 #{ + */ + public static final String SYS_VAR_PREFIX = "#{"; + + /** + * 符号 {{ + */ + public static final String DOUBLE_LEFT_CURLY_BRACKET = "{{"; + + /** + * 符号:[ + */ + public static final String SQUARE_BRACKETS_LEFT = "["; + /** + * 符号:] + */ + public static final String SQUARE_BRACKETS_RIGHT = "]"; + +} \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java index e4ae69a..319ae69 100644 --- a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java +++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/SqlInjectionUtil.java @@ -4,28 +4,48 @@ import cn.hutool.crypto.SecureUtil; import lombok.extern.slf4j.Slf4j; import org.jeecg.common.exception.JeecgBootException; import javax.servlet.http.HttpServletRequest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * sql注入处理工具类 - * + * * @author zhoujf */ @Slf4j public class SqlInjectionUtil { + private final static String xssStr = "and |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()"; + + /** + * 正则 user() 匹配更严谨 + */ + private final static String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)"; + /**正则 show tables*/ + private final static String SHOW_TABLES = "show\\s+tables"; + + /** + * sleep函数 + */ + private final static Pattern FUN_SLEEP = Pattern.compile("sleep\\([\\d\\.]*\\)"); + + /** + * sql注释的正则 + */ + private final static Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/"); + /** * sign 用于表字典加签的盐值【SQL漏洞】 * (上线修改值 20200501,同步修改前端的盐值) */ private final static String TABLE_DICT_SIGN_SALT = "20200501"; - private final static String xssStr = "'|and |exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+"; /* - * 针对表字典进行额外的sign签名校验(增加安全机制) - * @param dictCode: - * @param sign: - * @param request: - * @Return: void - */ + * 针对表字典进行额外的sign签名校验(增加安全机制) + * @param dictCode: + * @param sign: + * @param request: + * @Return: void + */ public static void checkDictTableSign(String dictCode, String sign, HttpServletRequest request) { //表字典SQL注入漏洞,签名校验 String accessToken = request.getHeader("X-Access-Token"); @@ -41,7 +61,7 @@ public class SqlInjectionUtil { /** * sql注入过滤处理,遇到注入关键字抛异常 - * + * * @param value * @return */ @@ -64,7 +84,7 @@ public class SqlInjectionUtil { /** * sql注入过滤处理,遇到注入关键字抛异常 - * + * * @param values * @return */ @@ -112,11 +132,11 @@ public class SqlInjectionUtil { } - /** - * @特殊方法(不通用) 仅用于Online报表SQL解析,注入过滤 - * @param value - * @return - */ + /** + * @特殊方法(不通用) 仅用于Online报表SQL解析,注入过滤 + * @param value + * @return + */ @Deprecated public static void specialFilterContentForOnlineReport(String value) { String specialXssStr = " exec | insert | delete | update | drop | chr | mid | master | truncate | char | declare |"; @@ -136,4 +156,59 @@ public class SqlInjectionUtil { return; } + /** + * 【提醒:不通用】 + * 仅用于字典条件SQL参数,注入过滤 + * + * @param value + * @return + */ + //@Deprecated + public static void specialFilterContentForDictSql(String value) { + String specialXssStr = " exec |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract| insert | select | delete | update | drop | count | chr | mid | master | truncate | char | declare |;|+|user()"; + String[] xssArr = specialXssStr.split("\\|"); + if (value == null || "".equals(value)) { + return; + } + // 校验sql注释 不允许有sql注释 + checkSqlAnnotation(value); + // 统一转为小写 + value = value.toLowerCase(); + //SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE + //value = value.replaceAll("/\\*.*\\*/",""); + + for (int i = 0; i < xssArr.length; i++) { + if (value.indexOf(xssArr[i]) > -1 || value.startsWith(xssArr[i].trim())) { + log.error("请注意,存在SQL注入关键词---> {}", xssArr[i]); + log.error("请注意,值可能存在SQL注入风险!---> {}", value); + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){ + throw new RuntimeException("请注意,值可能存在SQL注入风险!--->" + value); + } + return; + } + + + /** + * 校验是否有sql注释 + * @return + */ + public static void checkSqlAnnotation(String str){ + Matcher matcher = SQL_ANNOTATION.matcher(str); + if(matcher.find()){ + String error = "请注意,值可能存在SQL注入风险---> \\*.*\\"; + log.error(error); + throw new RuntimeException(error); + } + + // issues/4737 sys/duplicate/check SQL注入 #4737 + Matcher sleepMatcher = FUN_SLEEP.matcher(str); + if(sleepMatcher.find()){ + String error = "请注意,值可能存在SQL注入风险---> sleep"; + log.error(error); + throw new RuntimeException(error); + } + } } diff --git a/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java new file mode 100644 index 0000000..77829fb --- /dev/null +++ b/jeecg-boot/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/security/AbstractQueryBlackListHandler.java @@ -0,0 +1,231 @@ +package org.jeecg.common.util.security; + +import lombok.extern.slf4j.Slf4j; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 查询表/字段 黑名单处理 + * @Author taoYan + * @Date 2022/3/17 11:21 + **/ +@Slf4j +public abstract class AbstractQueryBlackListHandler { + + /** + * key-表名 + * value-字段名,多个逗号隔开 + * 两种配置方式-- 全部配置成小写 + * ruleMap.put("sys_user", "*")sys_user所有的字段不支持查询 + * ruleMap.put("sys_user", "username,password")sys_user中的username和password不支持查询 + */ + public static Map ruleMap = new HashMap<>(); + + /** + * 以下字符不能出现在表名中或是字段名中 + */ + public static final Pattern ILLEGAL_NAME_REG = Pattern.compile("[-]{2,}"); + + static { + ruleMap.put("sys_user", "password,salt"); + } + + + /** + * 根据 sql语句 获取表和字段信息,需要到具体的实现类重写此方法- + * 不同的场景 处理可能不太一样 需要自定义,但是返回值确定 + * @param sql + * @return + */ + protected abstract List getQueryTableInfo(String sql); + + + /** + * 校验sql语句 成功返回true + * @param sql + * @return + */ + public boolean isPass(String sql) { + List list = null; + //【jeecg-boot/issues/4040】在线报表不支持子查询,解析报错 #4040 + try { + list = this.getQueryTableInfo(sql.toLowerCase()); + } catch (Exception e) { + log.warn("校验sql语句,解析报错:{}",e.getMessage()); + } + + if(list==null){ + return true; + } + log.info("--获取sql信息--", list.toString()); + boolean flag = checkTableAndFieldsName(list); + if(flag == false){ + return false; + } + for (QueryTable table : list) { + String name = table.getName(); + String fieldString = ruleMap.get(name); + // 有没有配置这张表 + if (fieldString != null) { + if ("*".equals(fieldString) || table.isAll()) { + flag = false; + log.warn("sql黑名单校验,表【"+name+"】禁止查询"); + break; + } else if (table.existSameField(fieldString)) { + flag = false; + break; + } + + } + } + return flag; + } + + /** + * 校验表名和字段名是否有效,或是是否会带些特殊的字符串进行sql注入 + * issues/4983 SQL Injection in 3.5.1 #4983 + * @return + */ + private boolean checkTableAndFieldsName(List list){ + boolean flag = true; + for(QueryTable queryTable: list){ + String tableName = queryTable.getName(); + if(hasSpecialString(tableName)){ + flag = false; + log.warn("sql黑名单校验,表名【"+tableName+"】包含特殊字符"); + break; + } + Set fields = queryTable.getFields(); + for(String name: fields){ + if(hasSpecialString(name)){ + flag = false; + log.warn("sql黑名单校验,字段名【"+name+"】包含特殊字符"); + break; + } + } + } + return flag; + } + + /** + * 是否包含特殊的字符串 + * @param name + * @return + */ + private boolean hasSpecialString(String name){ + Matcher m = ILLEGAL_NAME_REG.matcher(name); + if (m.find()) { + return true; + } + return false; + } + + + /** + * 查询的表的信息 + */ + protected class QueryTable { + //表名 + private String name; + //表的别名 + private String alias; + // 字段名集合 + private Set fields; + // 是否查询所有字段 + private boolean all; + + public QueryTable() { + } + + public QueryTable(String name, String alias) { + this.name = name; + this.alias = alias; + this.all = false; + this.fields = new HashSet<>(); + } + + public void addField(String field) { + this.fields.add(field); + } + + public String getName() { + return name; + } + + public Set getFields() { + return new HashSet<>(fields); + } + + public void setName(String name) { + this.name = name; + } + + public void setFields(Set fields) { + this.fields = fields; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public boolean isAll() { + return all; + } + + public void setAll(boolean all) { + this.all = all; + } + + /** + * 判断是否有相同字段 + * + * @param fieldString + * @return + */ + public boolean existSameField(String fieldString) { + String[] arr = fieldString.split(","); + for (String exp : fields) { + for (String config : arr) { + if (exp.equals(config)) { + // 非常明确的列直接比较 + log.warn("sql黑名单校验,表【"+name+"】中字段【"+config+"】禁止查询"); + return true; + } else { + // 使用表达式的列 只能判读字符串包含了 + String aliasColumn = config; + if (alias != null && alias.length() > 0) { + aliasColumn = alias + "." + config; + } + if (exp.indexOf(aliasColumn) > 0) { + log.warn("sql黑名单校验,表【"+name+"】中字段【"+config+"】禁止查询"); + return true; + } + } + } + } + return false; + } + + @Override + public String toString() { + return "QueryTable{" + + "name='" + name + '\'' + + ", alias='" + alias + '\'' + + ", fields=" + fields + + ", all=" + all + + '}'; + } + } + + public String getError(){ + // TODO + return "系统设置了安全规则,敏感表和敏感字段禁止查询,联系管理员授权!"; + } + +} diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/controller/SystemAPIController.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/controller/SystemAPIController.java index 98cecce..e9a8814 100644 --- a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/controller/SystemAPIController.java +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/controller/SystemAPIController.java @@ -1,10 +1,13 @@ package org.jeecg.modules.api.controller; import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; import org.jeecg.common.api.dto.message.*; import org.jeecg.common.api.dto.OnlineAuthDTO; import org.jeecg.common.system.api.ISysBaseAPI; import org.jeecg.common.system.vo.*; +import org.jeecg.common.util.SqlInjectionUtil; +import org.jeecg.modules.system.security.DictQueryBlackListHandler; import org.jeecg.modules.system.service.ISysUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @@ -17,6 +20,7 @@ import java.util.Set; /** * 服务化 system模块 对外接口请求类 */ +@Slf4j @RestController @RequestMapping("/sys/api") public class SystemAPIController { @@ -26,6 +30,8 @@ public class SystemAPIController { @Autowired private ISysUserService sysUserService; + @Autowired + private DictQueryBlackListHandler dictQueryBlackListHandler; /** @@ -167,6 +173,11 @@ public class SystemAPIController { */ @GetMapping("/queryTableDictItemsByCode") List queryTableDictItemsByCode(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code){ + String str = table+","+text+","+code; + if(!dictQueryBlackListHandler.isPass(str)){ + log.error(dictQueryBlackListHandler.getError()); + return null; + } return sysBaseAPI.queryTableDictItemsByCode(table, text, code); } @@ -190,6 +201,14 @@ public class SystemAPIController { */ @GetMapping("/queryFilterTableDictInfo") List queryFilterTableDictInfo(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("filterSql") String filterSql){ + String str = table+","+text+","+code; + if(!dictQueryBlackListHandler.isPass(str)){ + log.error(dictQueryBlackListHandler.getError()); + return null; + } + String[] arr = new String[]{table, text, code}; + SqlInjectionUtil.filterContent(arr); + SqlInjectionUtil.specialFilterContentForDictSql(filterSql); return sysBaseAPI.queryFilterTableDictInfo(table, text, code, filterSql); } @@ -204,6 +223,11 @@ public class SystemAPIController { @Deprecated @GetMapping("/queryTableDictByKeys") public List queryTableDictByKeys(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("keyArray") String[] keyArray){ + String str = table+","+text+","+code; + if(!dictQueryBlackListHandler.isPass(str)){ + log.error(dictQueryBlackListHandler.getError()); + return null; + } return sysBaseAPI.queryTableDictByKeys(table, text, code, keyArray); } @@ -457,6 +481,13 @@ public class SystemAPIController { */ @GetMapping("/translateDictFromTable") public String translateDictFromTable(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("key") String key){ + String str = table+","+text+","+code; + if(!dictQueryBlackListHandler.isPass(str)){ + log.error(dictQueryBlackListHandler.getError()); + return null; + } + String[] arr = new String[]{table, text, code, key}; + SqlInjectionUtil.filterContent(arr); return sysBaseAPI.translateDictFromTable(table, text, code, key); } diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/DuplicateCheckController.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/DuplicateCheckController.java index c9de283..34a5199 100644 --- a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/DuplicateCheckController.java +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/DuplicateCheckController.java @@ -4,9 +4,11 @@ import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.jeecg.common.api.vo.Result; +import org.jeecg.common.constant.SymbolConstant; import org.jeecg.common.util.SqlInjectionUtil; import org.jeecg.modules.system.mapper.SysDictMapper; import org.jeecg.modules.system.model.DuplicateCheckVo; +import org.jeecg.modules.system.security.DictQueryBlackListHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @@ -31,6 +33,8 @@ public class DuplicateCheckController { @Autowired SysDictMapper sysDictMapper; + @Autowired + DictQueryBlackListHandler dictQueryBlackListHandler; /** * 校验数据是否在系统中是否存在 @@ -47,6 +51,18 @@ public class DuplicateCheckController { //SQL注入校验(只限制非法串改数据库) final String[] sqlInjCheck = {duplicateCheckVo.getTableName(),duplicateCheckVo.getFieldName()}; SqlInjectionUtil.filterContent(sqlInjCheck); + if(StringUtils.isEmpty(duplicateCheckVo.getFieldVal())){ + Result rs = new Result(); + rs.setCode(500); + rs.setSuccess(true); + rs.setMessage("数据为空,不作处理!"); + return rs; + } + String checkSql = duplicateCheckVo.getTableName() + SymbolConstant.COMMA + duplicateCheckVo.getFieldName() + SymbolConstant.COMMA; + if(!dictQueryBlackListHandler.isPass(checkSql)){ + return Result.error(dictQueryBlackListHandler.getError()); + } + if (StringUtils.isNotBlank(duplicateCheckVo.getDataId())) { // [2].编辑页面校验 num = sysDictMapper.duplicateCheckCountSql(duplicateCheckVo); diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDictController.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDictController.java index 367a858..be8d0ba 100644 --- a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDictController.java +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/controller/SysDictController.java @@ -23,6 +23,7 @@ import org.jeecg.modules.system.entity.SysDict; import org.jeecg.modules.system.entity.SysDictItem; import org.jeecg.modules.system.model.SysDictTree; import org.jeecg.modules.system.model.TreeSelectModel; +import org.jeecg.modules.system.security.DictQueryBlackListHandler; import org.jeecg.modules.system.service.ISysDictItemService; import org.jeecg.modules.system.service.ISysDictService; import org.jeecg.modules.system.vo.SysDictPage; @@ -64,6 +65,8 @@ public class SysDictController { private ISysDictItemService sysDictItemService; @Autowired public RedisTemplate redisTemplate; + @Autowired + private DictQueryBlackListHandler dictQueryBlackListHandler; @RequestMapping(value = "/list", method = RequestMethod.GET) public Result> queryPageList(SysDict sysDict,@RequestParam(name="pageNo", defaultValue="1") Integer pageNo, @@ -140,6 +143,9 @@ public class SysDictController { public Result> getDictItems(@PathVariable String dictCode, @RequestParam(value = "sign",required = false) String sign,HttpServletRequest request) { log.info(" dictCode : "+ dictCode); Result> result = new Result>(); + if(!dictQueryBlackListHandler.isPass(dictCode)){ + return result.error500(dictQueryBlackListHandler.getError()); + } try { List ls = sysDictService.getDictItems(dictCode); if (ls == null) { @@ -204,6 +210,9 @@ public class SysDictController { @RequestParam(value = "pageSize", required = false) Integer pageSize) { log.info(" 加载字典表数据,加载关键字: "+ keyword); Result> result = new Result>(); + if(!dictQueryBlackListHandler.isPass(dictCode)){ + return result.error500(dictQueryBlackListHandler.getError()); + } try { List ls = sysDictService.loadDict(dictCode, keyword, pageSize); if (ls == null) { @@ -274,6 +283,9 @@ public class SysDictController { @RequestMapping(value = "/loadDictItem/{dictCode}", method = RequestMethod.GET) public Result> loadDictItem(@PathVariable String dictCode,@RequestParam(name="key") String keys, @RequestParam(value = "sign",required = false) String sign,@RequestParam(value = "delNotExist",required = false,defaultValue = "true") boolean delNotExist,HttpServletRequest request) { Result> result = new Result<>(); + if(!dictQueryBlackListHandler.isPass(dictCode)){ + return result.error500(dictQueryBlackListHandler.getError()); + } try { if(dictCode.indexOf(",")!=-1) { String[] params = dictCode.split(","); @@ -318,6 +330,9 @@ public class SysDictController { // SQL注入漏洞 sign签名校验(表名,label字段,val字段,条件) String dictCode = tbname+","+text+","+code+","+condition; SqlInjectionUtil.filterContent(dictCode); + if(!dictQueryBlackListHandler.isPass(dictCode)){ + return result.error500(dictQueryBlackListHandler.getError()); + } List ls = sysDictService.queryTreeList(query,tbname, text, code, pidField, pid,hasChildField); result.setSuccess(true); result.setResult(ls); @@ -341,6 +356,9 @@ public class SysDictController { // SQL注入漏洞 sign签名校验 String dictCode = query.getTable()+","+query.getText()+","+query.getCode(); SqlInjectionUtil.filterContent(dictCode); + if(!dictQueryBlackListHandler.isPass(dictCode)){ + return res.error500(dictQueryBlackListHandler.getError()); + } List ls = this.sysDictService.queryDictTablePageList(query,pageSize,pageNo); res.setResult(ls); res.setSuccess(true); diff --git a/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/security/DictQueryBlackListHandler.java b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/security/DictQueryBlackListHandler.java new file mode 100644 index 0000000..d3c7669 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module-system/src/main/java/org/jeecg/modules/system/security/DictQueryBlackListHandler.java @@ -0,0 +1,59 @@ +package org.jeecg.modules.system.security; + +import org.jeecg.common.constant.SymbolConstant; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.common.util.security.AbstractQueryBlackListHandler; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * 字典组件 执行sql前校验 只校验表字典 + * dictCodeString格式如: + * table,text,code + * table where xxx,text,code + * table,text,code, where xxx + * + * @Author taoYan + * @Date 2022/3/23 21:10 + **/ +@Component("dictQueryBlackListHandler") +public class DictQueryBlackListHandler extends AbstractQueryBlackListHandler { + + @Override + protected List getQueryTableInfo(String dictCodeString) { + if (dictCodeString != null && dictCodeString.indexOf(SymbolConstant.COMMA) > 0) { + String[] arr = dictCodeString.split(SymbolConstant.COMMA); + if (arr.length != 3 && arr.length != 4) { + return null; + } + String tableName = getTableName(arr[0]); + QueryTable table = new QueryTable(tableName, ""); + // 无论什么场景 第二、三个元素一定是表的字段,直接add + table.addField(arr[1].trim()); + String filed = arr[2].trim(); + if (oConvertUtils.isNotEmpty(filed)) { + table.addField(filed); + } + List list = new ArrayList<>(); + list.add(table); + return list; + } + return null; + } + + /** + * 取where前面的为:table name + * + * @param str + * @return + */ + private String getTableName(String str) { + String[] arr = str.split("\\s+(?i)where\\s+"); + // sys_user , (sys_user), sys_user%20, %60sys_user%60 issues/4393 + String reg = "\\s+|\\(|\\)|`"; + return arr[0].replaceAll(reg, ""); + } + +}