MyBatis中傳參時為什么要用#{},這個問題和MyBatis如何防止SQL注入類似。不過在解釋這個問題之前,先解釋一下什么是SQL注入,還有些稱作注入攻擊這個問題。
SQL注入就是SQL 對傳入參數的拼接。sql語句是 String類型的,如果用 + 來拼接,表示的是直接操作這個String 類型的字符串,這是改變了sql的具體內容了,如果用#{id},表示的是操作字改變里面字段的參數值。
例如:
- 用+拼接的: "select * from user where code="+code+ " and password="+password;
那么code = “1233 or code=456” password="2123":
結果: select * from user where code='123' or code='456' and password=‘2123’那么這個sql的規則就亂了。
- 用#操作的 select * from user where code=#{code} and password=#{password};
那么code = “1233 or code=456” password="2123":
結果: select * from user where code=' 1233 or code=456 ' and password='2123'那么這個sql的規則還是老樣子。
綜上所述就是會發生拼接錯誤!其實應該是參數傳遞出現的問題,#{}傳遞參數時,會在傳遞的參數上加上引號,在傳遞屬性比如 password=? 時,可以很方便的使用#{password}。#{}傳遞的參數實際上是通過占位符(類似於JDBC中的?,在JDBC中會利用?去代替參數先進行編譯,然后代入參數執行,這樣就可以避免注入,因為注入不安全因素是發生在編譯期,用占位符后就避免了這個問題)去傳入到已經預編譯好的SQL中去的,所以此時的SQL已經完成編譯,只需要傳參數就完成執行了。如:
1 <select id =“ getBlogById ”resultType =“ Blog ”parameterType =“ int ”>
2 SELECT id,title,author,content
3 FROM blog 4 WHERE id =#{id} 5 </select>
在這里的id傳參就是使用了#{},這部分打印后會看到的SQL語句是:
1 SELECT id,title,author,content FROM blog WHERE id =?
就是不管輸入什么參數打印出的SQL都是這樣的。這是因為MyBatis的啟用了預編譯功能,在SQL執行前,會先將上面的SQL發送給數據庫進行編譯,執行時,直接使用編譯好的SQL ,替換占位符“?”就可以了。因為SQL注入只能對編譯過程起作用,所以這樣的方式就很好地避免了SQL注入的問題。
具體可以看底層實現:其原理就是采用了JDBC的PreparedStatement。就會將sql語句:“select id,no from user where id =?” 預先編譯好,也就是SQL引擎會預先進行語法分析,產生語法樹,生成執行計划,也就是說,后面你輸入的參數,無論你輸入的是什么,都不會影響該SQL語句的語法結構了,因為語法分析已經完成了,而語法分析主要是分析sql命令,比如select,from,where,and,or,order by等等。所以即使你后面輸入了這些sql命令,也不會被當成sql命令來執行了,因為這些SQL命令的執行,必須先得通過語法分析,生成執行計划,既然語法分析已經完成,已經預編譯過了,那么后面輸入的參數,是絕對不可能作為SQL命令來執行的,只會被當做字符串字面值參數。所以的sql語句預編譯可以防御SQL注入。而且在多次執行同一個SQL時,能夠提高效率。原因是SQL已編譯好,再次執行時無需再編譯。
同樣的,想要避免SQL注入,只能使用上述的#{}結構,在MyBatis中,還有一種結構是${},使用該結構就不會避免注入。因為${}不會添加引號,傳遞的是什么就會直接放到SQL中去執行。
簡單滴說:# {}是經過預編譯的,是安全的 ;$ {}是未經過預編譯的,僅僅是取變量的值,是非安全的,存在SQL注入。
參考:
- https://www.cnblogs.com/jy107600/p/7097983.html
- https://bbs.csdn.net/topics/391069848
- https://blog.csdn.net/weixin_40773255/article/details/80426307
- https://blog.csdn.net/weixin_39986856/article/details/83651847