mybatis自定义@{}符号

您所在的位置:网站首页 mysql字段名可以用符号嘛 mybatis自定义@{}符号

mybatis自定义@{}符号

2023-06-05 16:42| 来源: 网络整理| 查看: 265

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录 前言一、研究一下DynamicTableNameParser的原理1.mybatis的plugin功能 二、实现自己的插件和sql解析器总结

前言 工作中,mybatis的${}如果能切实避免sql注入,还是很好用的,比如动态字段、动态表名,但代码安全扫描可不管能不能避免,见到${}直接判定高风险,甚是恼人啊。我这几天就遇到了,动态表名功能可以用mybatis-plus里的DynamicTableNameParser类实现,但我用的是mybatis,框架不宜轻易更换,没办法只能研究DynamicTableNameParser,模仿着自己写一个动态表名的功能,然后赌气式的自定义了一个@{}替换符,我就不信你安全扫描还能扫出@{}

提示:以下是本篇文章正文内容,下面案例可供参考

一、研究一下DynamicTableNameParser的原理 1.mybatis的plugin功能

mybatis的plugin是针对于mybatis四大组件(Statementhandler、resultsethandler、parameterHandler、executor)做增强操作的,相当于spring中的AOP,平时用的最多的分页插件PageHelper就是通过plugin实现的,如以下mybatis-plus自带的分页插件代码

@Setter @Deprecated @Accessors(chain = true) @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor {

实现Interceptor 接口,加上@Intercepts注解即可实现plugin功能,@Signature配置拦截规则,可配置多个,type只能配四大上述四大对象中的一个,配其他无效,method配对应的对象里的方法,args配方法的所有参数(不要多配或者遗漏,否则报错,找不到拦截的方法)。当程序执行到StatementHandler的prepare方法时,就会被拦截,执行PaginationInterceptor的intercept方法

@Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); // SQL 解析 this.sqlParser(metaObject);

intercept方法的其他部分未粘贴进来,感兴趣的可以看下源码,继续跟sqlParser方法,调用AbstractSqlParserHandler里的sqlParser方法

public abstract class AbstractSqlParserHandler { //在应用程序里,传入DynamicTableNameParser private List sqlParserList; private ISqlParserFilter sqlParserFilter; /** * 拦截 SQL 解析执行 */ protected void sqlParser(MetaObject metaObject) { if (null != metaObject) { Object originalObject = metaObject.getOriginalObject(); StatementHandler statementHandler = PluginUtils.realTarget(originalObject); metaObject = SystemMetaObject.forObject(statementHandler); if (null != this.sqlParserFilter && this.sqlParserFilter.doFilter(metaObject)) { return; } // @SqlParser(filter = true) 跳过该方法解析 if (SqlParserHelper.getSqlParserInfo(metaObject)) { return; } // SQL 解析 if (CollectionUtils.isNotEmpty(this.sqlParserList)) { // 好像不用判断也行,为了保险起见,还是加上吧. statementHandler = metaObject.hasGetter("delegate") ? (StatementHandler) metaObject.getValue("delegate") : statementHandler; if (!(statementHandler instanceof CallableStatementHandler)) { // 标记是否修改过 SQL boolean sqlChangedFlag = false; String originalSql = (String) metaObject.getValue(PluginUtils.DELEGATE_BOUNDSQL_SQL); for (ISqlParser sqlParser : this.sqlParserList) { //使用DynamicTableNameParser if (sqlParser.doFilter(metaObject, originalSql)) { SqlInfo sqlInfo = sqlParser.parser(metaObject, originalSql); if (null != sqlInfo) { originalSql = sqlInfo.getSql(); sqlChangedFlag = true; } } } if (sqlChangedFlag) { metaObject.setValue(PluginUtils.DELEGATE_BOUNDSQL_SQL, originalSql); } } } } } }

重点看这里 for (ISqlParser sqlParser : this.sqlParserList) {,sqlParserList里装的是一系列的实现了ISqlParser接口的解析器,SqlInfo sqlInfo = sqlParser.parser(metaObject, originalSql);这一行实现了sql里表名的替换

public class DynamicTableNameParser implements ISqlParser { //String为需要替换的表名,ITableNameHandler为动态表名处理器,即替换规则 private Map tableNameHandlerMap; @Override public SqlInfo parser(MetaObject metaObject, String sql) { Assert.isFalse(CollectionUtils.isEmpty(tableNameHandlerMap), "tableNameHandlerMap is empty."); if (allowProcess(metaObject)) { //将sql语句按空格切开成一个一个单词,然后通过from join等关键字确定表名 Collection tables = new TableNameParser(sql).tables(); if (CollectionUtils.isNotEmpty(tables)) { boolean sqlParsed = false; String parsedSql = sql; for (final String table : tables) { ITableNameHandler tableNameHandler = tableNameHandlerMap.get(table); if (null != tableNameHandler) { //找到了该表名的替换器,process方法里替换 parsedSql = tableNameHandler.process(metaObject, parsedSql, table); sqlParsed = true; } } if (sqlParsed) { return SqlInfo.newInstance().setSql(parsedSql); } } } return null; } public boolean allowProcess(MetaObject metaObject) { return true; } }

感兴趣的朋友可以网上搜一下mybaits-plus实现动态表名的方法

二、实现自己的插件和sql解析器

按照mybatis-plus的步骤指引,一步步实现即可,先定义plugin,然后定义sql解析器,把mybatis-plus能用的类copy过来,不报错就差不多了。接下来引入springboot,将plugin连带着sql解析器注入进spring容器,建mapper即可验证,上代码,gitee地址:https://gitee.com/wangchaoam/mybatisCustomLabel.git 代码结构图

总结 因看不惯银行对${}的一刀切,搞出了@{}来代替 ${},其实最简单的方法还是改mybatis的源码,不用写这么多代码,不用花时间搞懂mybatis-plus里大佬写的代码,只要将@{}加到解析器里,地位等同于 ${}即可,但maven库又被锁死,休想上传自己的jar包。如有问题,欢迎提出,人生中第一篇博文,分享一下这两天的成功


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3