前言
下圖為兩條sql:
從圖上可以看出 mu.row_id in(You can't use 'macro parameter character #' in math mode{userId}) 和 wwlr.LabelId in(#{userId}),其中userId是傳進來一個字符串類型的參數,參數的樣子是這樣的“4,44,514”,問題就出在這個參數傳進來后#和處理的方式是不一樣的。
區別
1、#{ }是預編譯處理,MyBatis在處理#{ }時,它會將sql中的#{ }替換為?,然后調用PreparedStatement的set方法來賦值,傳入字符串后,會在值兩邊加上單引號,如上面的值 “4,44,514”就會變成“ '4,44,514' ”;
2、${ }是字符串替換,在處理{ }是字符串替換, MyBatis在處理{ }時,它會將sql中的${ }替換為變量的值,傳入的數據不會加兩邊加上單引號。
注意:使用${ }會導致sql注入,不利於系統的安全性!
SQL注入:
就是通過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。常見的有匿名登錄(在登錄框輸入惡意的字符串)、借助異常獲取數據庫信息等
應用場合:
1、#{ }:主要用戶獲取DAO中的參數數據,在映射文件的SQL語句中出現#{}表達式,底層會創建預編譯的SQL;
2、${ }:主要用於獲取配置文件數據接口中的參數信息當{ }:主要用於獲取配置文件數據,DAO接口中的參數信息,當出現在映射文件的SQL語句中時創建的不是預編譯的SQL,而是字符串的拼接,有可能會導致SQL注入問題.所以一般使用$接收dao參數時,這些參數一般是字段名,表名等,例如order by {column}。
注:
${}獲取DAO參數數據時,參數必須使用@param注解進行修飾或者使用下標或者參數#{param1}形式;
#{}獲取DAO參數數據時,假如參數個數多於一個可有選擇的使用@param。
解決辦法
1、快速解決
最快的方法就是把#直接替換成$,這樣問題應該就可以解決了。
但是,我很無語,我確沒有解決。
本地跑代碼一點問題都沒有,部署到公司的docker上問題一樣沒解決,給人的感覺就是代碼根本沒有從#變$。
大家都知道其實是有危險性,會容易被注入,具我所知道,我們公司的是會加一層防止注入的功能,所以不知道是不是這個功能把的其實是有危險性,會容易被sql注入,具我所知道,我們公司的docker是會加一層防止 sql注入的功能 ,所以不知道是不是這個功能把的無效掉了。
當然,我也沒有去再到服務上打出sql來看一下,因為本來$就是不太安全的,所以我換了一種方式處理。
2、foreach標簽的使用
<select id="getUser" resultType="UserBean"> select * from md_user where id in <foreach collection="userIds" item="ids" open="(" close=")" separator=","> #{ids} </foreach> </select>
foreach標簽的屬性:
item:表示集合中每一個元素進行迭代時的別名。
index: 指定一個名稱,表示在迭代的過程中,每次迭代到的位置。
open:表示該語句以什么開始(既然是in條件語句,必然是 ' ( ' 開始)
separator: 表示每次進行迭代的時候以什么符號作為分隔符(既然是in條件語句,必然是 ' , ' 分隔)
close: 表示該語句以什么結束(既然是in條件語句,必然是 ' ) ' 結束)
collection:最關鍵,並且最容易出錯的屬性。需注意,該屬性必須指定,不同情況下,該屬性值是不同的,主要有三種情況:
若傳入的參數是單參數且類型為一個List的時候,屬性值為list;
若傳入的參數是單參數且類型為一個數組的時候,屬性值為array;
若傳入的參數為多參數時,就需要封裝為一個map集合進行處理。屬性值為Map的key;
foreach標簽主要用於構建in條件,他可以在sql中對集合進行迭代。
那對於我們項目中的改造,其實就是把原來傳進來的字符型參數變成List,這樣問題就完美的解決了,既實現了我們的功能 ,又解決了安全性問題。