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

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

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

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

      二、PreparedStatement 
    SQL 語句被預編譯並存儲在 PreparedStatement 對象中。而後可使用此對象屢次高效地執行該語句。 
    能夠經過調用 Connection 對象的 preparedStatement() 方法獲取 PreparedStatement 對象 
    PreparedStatement 對象所執行的 SQL 語句中,參數用問號(?)來表示,調用 PreparedStatement 對象的 setXXX() 方法來設置這些參數. setXXX() 方法有兩個參數,第一個參數是要設置的 SQL 語句中的參數的索引(從 1 開始),第二個是設置的 SQL 語句中的參數的值,注意用setXXX方式設置時,須要與數據庫中的字段類型對應,例如mysql中字段爲varchar,就須要使用setString方法,若是爲Date類型,就須要使用setDate方法來設置具體sql的參數。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

相關文章
相關標籤/搜索