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>

 

posted @ 2020-11-06 16:44  忘忧山的兰木  阅读(8103)  评论(0编辑  收藏  举报
她只是想吃这个而已啊……这一定是她非常爱吃的,我居然连如此细微的幸福也夺走了……
Hide
Switch
Save