前面介紹了各類SQL語句的調用過程,雖然例子代碼寫死了每一個SQL串,可是徹底能夠把查詢條件做爲方法參數傳進來。好比如今想刪除某個課程的教師記錄,那麼在編寫刪除方法時,就把課程名稱做爲該方法的一個輸入參數。據此編寫的方法代碼示例以下:html
// 刪除記錄 private static void deleteRecord(Statement stmt, String course) throws SQLException { String sql = String.format("delete from teacher where course='%s'", course); int count = stmt.executeUpdate(sql); // 執行處理語句 System.out.println("待執行的SQL語句:"+sql); System.out.println("刪除記錄語句的返回結果爲"+count); }
接着外部準備調用上面的deleteRecord方法,第二個課程參數填「化學」,表示但願刪除全部化學老師的記錄,調用代碼以下所示:mysql
deleteRecord(stmt, "化學"); // 刪除記錄
運行包含以上代碼的測試程序,觀察到如下的輸出日誌。sql
待執行的SQL語句:delete from teacher where course='化學' 刪除記錄語句的返回結果爲1
從日誌信息可見,只刪除一條化學老師的記錄,看起來彷佛一切正常。不過課程參數由外部傳入,誰知道課程字符串是什麼東西呢?假若有人閒得發慌,在鍵盤上隨便輸了幾個字符,像「' or '1'='1」這樣的字符串看成課程名稱。因而刪除方法的調用代碼變成了下面這般:數據庫
deleteRecord(stmt, "' or '1'='1"); // 刪除記錄
再次運行測試程序,發現輸出日誌變得有點不對勁:安全
待執行的SQL語句:delete from teacher where course='' or '1'='1' 刪除記錄語句的返回結果爲4
沒想到隨便輸的幾個字符居然也讓SQL語句執行了,並且是把teacher表的剩餘記錄刪得精光。這可不得了了,原語句的格式明明只肯刪除特定課程的記錄,爲啥執行結果截然不同呢?原因在於待執行的SQL語句呆板地將課程字符串原樣填了進去,形成出現「or '1'='1'」這種極端條件,天然MySQL忠實地刪光了teacher表。諸如此類的SQL缺陷,人稱SQL注入漏洞,它經常被黑客利用隨心所欲,形成重大損失。
上述的實驗結果暴露了報告機制的安全問題,一旦條件參數被人惡意篡改,就可能產生意料以外的嚴重情況。爲此JDBC又設計了另外一種預報告機制,預報告定義了新類PreparedStatement,與原報告Statement不一樣的是,建立預報告對象時就要設定SQL語句,而且SQL裏面的動態參數以問號代替。而後準備調用executeUpdate或者executeQuery以前,先調用預報告對象的setString方法來設置對應序號的參數值。下面即是引入預報告以後的數據庫操做代碼例子:測試
// 測試預報告的處理 private static void testPreparedStatement() { String sql = "delete from teacher where course=?"; // 先獲取數據庫的鏈接,再建立鏈接的預報告 try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword); PreparedStatement stmt = conn.prepareStatement(sql)) { //stmt.setString(1, "化學"); // 設置對應序號的參數值 stmt.setString(1, "'' or 1=1"); // 設置對應序號的參數值 int count = stmt.executeUpdate(); // 執行處理語句 System.out.println("預先準備的SQL語句:"+stmt.toString()); System.out.println("刪除記錄語句的返回結果爲"+count); } catch (SQLException e) { e.printStackTrace(); } }
仍以以前的惡意字符串爲例,上面代碼在調用setString方法時填入了「' or '1'='1」,意圖繼續渾水摸魚。運行包含testPreparedStatement方法的測試程序,觀察到的日誌信息以下所示:設計
預先準備的SQL語句:com.mysql.cj.jdbc.ClientPreparedStatement: delete from teacher where course='\'\' or 1=1' 刪除記錄語句的返回結果爲0
從日誌結果可見,此次搗亂行爲沒有得逞,一條記錄都沒刪除。注意此時的條件語句變爲「course='\'\' or 1=1'」,顯然預報告把字符串中的單引號作了轉義,使得轉義後的條件語句格式不正確,也就沒能成功執行SQL。由此證實預報告PreparedStatement提高了數據庫操做的安全性,凡是須要動態傳入參數的SQL語句,最好採起預報告機制加以處理。日誌
更多Java技術文章參見《Java開發筆記(序)章節目錄》orm