JDBC編程之預編譯SQL與防注入式攻擊以及PreparedStatement的使用教程


    轉載請注明原文地址: http://www.cnblogs.com/ygj0930/p/5876951.html

     在JDBC編程中,常用Statement、PreparedStatement 和 CallableStatement三種方式來執行查詢語句,其中 Statement 用於通用查詢, PreparedStatement 用於執行參數化查詢,而 CallableStatement則是用於存儲過程。

      1、Statement 
      該對象用於執行靜態的 SQL 語句,並且返回執行結果。 此處的SQL語句必須是完整的,有明確的數據指示。查的是哪條記錄?改的是哪條記錄?都要指示清楚。
     通過調用 Connection 對象的 createStatement 方法創建該對象 
查詢:ResultSet excuteQuery(String sql)——返回查詢結果的封裝對象ResultSet. 用next()遍歷結果集,getXX()獲取記錄數據。
修改、刪除、增加:int excuteUpdate(String sql)——返回影響的數據表記錄數. 

      2、PreparedStatement 
    SQL 語句被預編譯並存儲在 PreparedStatement 對象中。然后可以使用此對象多次高效地執行該語句。 
    可以通過調用 Connection 對象的 preparedStatement() 方法獲取 PreparedStatement 對象 
    PreparedStatement 對象所執行的 SQL 語句中,參數用問號(?)來表示,調用 PreparedStatement 對象的 setXXX() 方法來設置這些參數. setXXX() 方法有兩個參數,第一個參數是要設置的 SQL 語句中的參數的索引(從 1 開始),第二個是設置的 SQL 語句中的參數的值,注意用setXXX方式設置時,需要與數據庫中的字段類型對應,例如mysql中字段為varchar,就需要使用setString方法,如果為Date類型,就需要使用setDate方法來設置具體sql的參數。

    簡單來說就是,預編譯的SQL語句不是有具體數值的語句,而是用(?)來代替具體數據,然后在執行的時候再調用setXX()方法把具體的數據傳入。同時,這個語句只在第一次執行的時候編譯一次,然后保存在緩存中。之后執行時,只需從緩存中抽取編譯過了的代碼以及新傳進來的具體數據,即可獲得完整的sql命令。這樣一來就省下了后面每次執行時語句的編譯時間。

 

    使用預編譯分4步走:

    1:定義預編譯的sql語句,其中待填入的參數用  ?  占位。注意,?無關類型,不需要加分號之類。其具體數據類型在下面setXX()時決定。

    2:創建預編譯Statement,並把sql語句傳入。此時sql語句已與此preparedStatement綁定。所以第4步執行語句時無需再把sql語句作為參數傳入execute()。

    3:填入具體參數。通過setXX(問號下標,數值)來為sql語句填入具體數據。注意:問號下標從1開始,setXX與數值類型有關,字符串就是setString(index,str).

    4:執行預處理對象。主要有:

 boolean execute()
          在此 PreparedStatement 對象中執行 SQL 語句,該語句可以是任何種類的 SQL 語句。
 ResultSet executeQuery()
          在此 PreparedStatement 對象中執行 SQL 查詢,並返回該查詢生成的 ResultSet 對象。
 int executeUpdate()
          在此 PreparedStatement 對象中執行 SQL 語句,該語句必須是一個 SQL 數據操作語言(Data Manipulation Language,DML)語句,比如 INSERTUPDATEDELETE 語句;或者是無返回內容的 SQL 語句,比如 DDL 語句。

    注意,前面創建preparedstatement時已經把sql語句傳入了,此時執行不需再把sql語句傳入,這是與一般statement執行sql語句所不同之處。

    比如:

     String sql="select Sname from stu where Sno=?"

     PreparedStatement prestmt = conn.prepareStatement(sql);

  prestmt.setString(1,sno);

    prestmt.executeQuery();

 

 

使用預編譯的好處:

1:PreparedStatement比 Statement 更快
使用 PreparedStatement 最重要的一點好處是它擁有更佳的性能優勢,SQL語句會預編譯在數據庫系統中。執行計划同樣會被緩存起來,它允許數據庫做參數化查詢。使用預處理語句比普通的查詢更快,因為它做的工作更少(數據庫對SQL語句的分析,編譯,優化已經在第一次查詢前完成了)。

2:PreparedStatement可以防止SQL注入式攻擊

SQL 注入攻擊:SQL 注入是利用某些系統沒有對用戶輸入的數據進行充分的檢查,而在用戶輸入數據中注入非法的 SQL 語句段或命令,從而利用系統的 SQL 引擎完成惡意行為的做法。

比如:某個網站的登錄驗證SQL查詢代碼為:

 

1
strSQL = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '" + passWord + "';"

惡意填入:

1
2
userName = "1' OR '1'='1" ;
passWord = "1' OR '1'='1" ;

那么最終SQL語句變成了:

1
strSQL = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"

因為WHERE條件恆為真,這就相當於執行:

1
strSQL = "SELECT * FROM users;"

因此可以達到無賬號密碼亦可登錄網站。

如果惡意用戶要是更壞一點,SQL語句變成

1
strSQL = "SELECT * FROM users WHERE name = 'any_value' and pw = ''; DROP TABLE users"

這樣一來,雖然沒有登錄,但是數據表都被刪除了。

     使用PreparedStatement的參數化的查詢可以阻止大部分的SQL注入。在使用參數化查詢的情況下,數據庫系統不會將參數的內容視為SQL指令的一部分來處理,而是在數據庫完成SQL指令的編譯后,才套用參數運行,因此就算參數中含有破壞性的指令,也不會被數據庫所運行。因為對於參數化查詢來說,查詢SQL語句的格式是已經規定好了的,需要查的數據也設置好了,缺的只是具體的那幾個數據而已。所以用戶能提供的只是數據,而且只能按需提供,無法更進一步做出影響數據庫的其他舉動來。

 

參考資料:

http://www.importnew.com/5006.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM