Mybatis动态指定表名、列名,如何防止SQL注入?
以下的代码,操作的是MySQL数据库
方式一
因为表名无法通过 CONCAT() 函数进行拼接,所以只能通过 ${} 直接将表名的字符串替换。
<select id="selectUnionALL" resultType="Dept"> SELECT * FROM ${tableName} WHERE deptno = #{deptno} </select>
但是会存在SQL的注入,比如:tableName = dept_01; delete from dept_01; 就会删除所有的数据。
解决方式:通过代码去判断传入的参数是否包含 delete、drop... 等危险操作。
方式二
假设存在数据库中存在 dept_01,dept_02,dept_03..... 这样命名的表,那么怎么做呢?
可以通过存储过程去解决动态指定表名的问题。
1)创建存储过程
DROP PROCEDURE IF EXISTS getName; -- 如果一创建存储过程则删除 DELIMITER $$ -- 定义结束字符,可以任意 -- 创建名称为getName的存储过程,需要传入的参数:tableName表名,deptno条件 CREATE PROCEDURE getName(IN tableName VARCHAR(100), IN deptno INT) BEGIN DECLARE tn VARCHAR(10); -- 创建变量保存表名 set tn = ( -- 查询MySQL中所有的表,然后根据 表所在的数据库 和 表的后缀名称 获取到对应的表名 select table_name from information_schema.TABLES where table_schema='db03' AND table_name LIKE CONCAT('dept_', '%') AND table_name LIKE CONCAT('%', tableName) ); -- 再拼接查询语句 set @sqlStr = CONCAT("SELECT * FROM ", tn," WHERE deptno = '", deptno, "'"); PREPARE sqlStr FROM @sqlStr; EXECUTE sqlStr; -- 执行 END $$ -- 传入参数:表名的后缀 部门id CALL getName('02', 5); -- 测试
测试结果,这样就不存在注入问题,因为表名被作为了条件。
2)接口编写
// Controller层 @GetMapping("selectUnionALL/db03") @ResponseBody public List<Dept> selectUnionALL() { Dept dept = new Dept(); dept.setDeptno(5L); dept.setTableName("02"); return deptService.selectUnionALL(dept); } // service层 public List<Dept> selectUnionALL(Dept dept) { return deptMapper.selectUnionALL(dept); } // mapper层 List<Dept> selectUnionALL(Dept dept);
3)mapper.xml 调用
<!-- statementType: 需要设置为 CALLABLE --> <select id="selectUnionALL" resultType="Dept" statementType="CALLABLE"> CALL getName(#{tableName}, #{deptno}); </select>
致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!