MyBatis中傳參時爲什麼要用#{}

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已經完成編譯,只需要傳參數就完成執行了。如:

<select id =“ getBlogById ”resultType =“ Blog ”parameterType =“ int ”>
        SELECT id,title,author,content
        FROM blog
        WHERE id =#{id}
 </select>

在這裏的id傳參就是使用了#{},這部分打印後會看到的SQL語句是:

 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注入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章