1. 程式人生 > 實用技巧 >Mybatis動態指定表名、列名,如何防止SQL注入?

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... 等危險操作。

方式二

可以通過儲存過程去解決動態指定表名的問題。

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 $$ 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>