一文搞懂Java?JDBC中的SQL注入問(wèn)題
??SQL注入
?什么是SQL注入
在用戶輸入的數(shù)據(jù)中有SQL關(guān)鍵字或語(yǔ)法,并且關(guān)鍵字或語(yǔ)法參與了SQL語(yǔ)句的編譯。導(dǎo)致SQL語(yǔ)句編譯后的條件為true,一直得到正確的結(jié)果。這種現(xiàn)象就是SQL注入。
?SQL注入的效果的演示
??SQL注入代碼
package cn.bdqn.demo03; ? import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Scanner; ? public class Login { ? public static void main(String[] args) throws ClassNotFoundException, SQLException { //創(chuàng)建Scanner類對(duì)象,從控制臺(tái)獲取用戶名和密碼數(shù)據(jù) Scanner sc = new Scanner(System.in); System.out.println("請(qǐng)輸入用戶名:"); String user = sc.nextLine();//使用nextLine()方法獲取字符串 System.out.println("請(qǐng)輸入密碼:"); String pwd = sc.nextLine();//使用nextLine()方法獲取字符串 //1、注冊(cè)驅(qū)動(dòng) Class.forName("com.mysql.jdbc.Driver"); //2、獲取連接對(duì)象 String url = "jdbc:mysql://127.0.0.1:3306/java221804"; String dbuser = "root"; String pssword = "123456"; Connection connection = DriverManager.getConnection(url, dbuser, pssword); //3、獲取發(fā)送SQL語(yǔ)句的對(duì)象 Statement statement =connection.createStatement(); //編寫(xiě)SQL語(yǔ)句 String sql = "SELECT * FROM user WHERE username='"+user+"' AND pssword = '"+pwd+"';"; //4、執(zhí)行SQL語(yǔ)句 ResultSet resultSet=statement.executeQuery(sql); if(resultSet.next()){ System.out.println("用戶名和密碼正確,登錄成功"); }else{ System.out.println("用戶名或密碼不正確,登錄失敗"); } //6、關(guān)閉資源 resultSet.close(); statement.close(); connection.close(); sc.close(); } }
??SQL注入效果
輸入錯(cuò)誤的用戶名和密碼,提示登錄失?。?/p>
輸入錯(cuò)誤的用戶名和密碼,提示登錄成功:產(chǎn)生了SQL注入
上面案例代碼中,當(dāng)你的用戶名為 abc' or 1=1;# 密碼為123,拼接到SQL語(yǔ)句中,變成如下效果:
SELECT * FROM user WHERE username='abc' or 1=1;#' AND pssword = '123';
此SQL語(yǔ)句or 后面1=1永遠(yuǎn)正確,#后面的成了注釋,所以這條語(yǔ)句會(huì)將表中所有的數(shù)據(jù)查詢出來(lái),然后再做數(shù)據(jù)判斷的時(shí)候,就會(huì)得到正確結(jié)果,從而說(shuō)用戶名和密碼正確,登錄成功。
?如何避免SQL注入
使用PreparedStatement代替Statement可以有效防止SQL注入的發(fā)生。由于SQL注入產(chǎn)生的原因是在用戶輸入數(shù)據(jù)對(duì)SQL整合,整合后再發(fā)送到數(shù)據(jù)庫(kù)進(jìn)行編譯產(chǎn)生的。
所以為了避免SQL注入,就需要SQL語(yǔ)句在用戶輸入數(shù)據(jù)前就進(jìn)行編譯,成為完整的SQL語(yǔ)句,編譯完成后再進(jìn)行數(shù)據(jù)填充。這個(gè)操作需要使用PrepareStatement實(shí)現(xiàn)。
PreparedStatement利用預(yù)編譯的機(jī)制將sql語(yǔ)句的主干和參數(shù)分別傳輸給數(shù)據(jù)庫(kù)服務(wù)器,從而使數(shù)據(jù)庫(kù)分辨的出哪些是sql語(yǔ)句的主干哪些是參數(shù),這樣一來(lái)即使參數(shù)中帶了sql的關(guān)鍵字,數(shù)據(jù)庫(kù)服務(wù)器也僅僅將他當(dāng)作參數(shù)值使用,關(guān)鍵字不會(huì)起作用,從而從原理上防止了sql注入的問(wèn)題。
??PrepareStatement解決SQL注入
PreparedStatement接口繼承了Statement接口,執(zhí)行SQL語(yǔ)句的方法與Statement執(zhí)行SQL語(yǔ)句的方法相同。
?PreparedStatement的應(yīng)用
PreparedStatement的作用:
- 預(yù)編譯SQL語(yǔ)句,效率高
- 安全,避免SQL注入
- 可以動(dòng)態(tài)的填充數(shù)據(jù),執(zhí)行多個(gè)同結(jié)構(gòu)的SQL語(yǔ)句
??參數(shù)標(biāo)記
//預(yù)編譯SQL語(yǔ)句,SQL中的所有參數(shù)由?符號(hào)占位,這被稱為參數(shù)標(biāo)記。在執(zhí)行SQL語(yǔ)句之前,必須為每個(gè)參數(shù)提供值。
String sql = "select * from user where userName = ? and pssword=?;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
??動(dòng)態(tài)參數(shù)綁定
preparedStatement.sexXxx(下標(biāo),值):參數(shù)下標(biāo)從1開(kāi)始,為指定參數(shù)下標(biāo)綁定值。Xxx表示數(shù)據(jù)類型。
//綁定參數(shù),有多少個(gè)?綁定多少個(gè)參數(shù)值
preparedStatement.setString(1, userName);
preparedStatement.setString(2, pwd);
?綜合案例
package cn.bdqn.demo02; ? import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Scanner; ? public class PreparedStatementDemo01 { ? public static void main(String[] args) throws ClassNotFoundException,SQLException { ? Scanner sc = new Scanner(System.in); System.out.println("請(qǐng)輸入用戶名:"); String userName = sc.nextLine(); System.out.println("請(qǐng)輸入密碼:"); String pwd = sc.nextLine(); ? // 1、注冊(cè)驅(qū)動(dòng) Class.forName("com.mysql.jdbc.Driver"); // 2、獲得連接 String url = "jdbc:mysql://localhost:3306/java2217"; String user = "root"; String pssword = "123456"; Connection connection = DriverManager.getConnection(url, user, pssword); // 3、獲取發(fā)送SQL對(duì)象 String sql = "select * from user where userName = ? and pssword=?;"; PreparedStatement preparedStatement = connection.prepareStatement(sql); // 4、綁定參數(shù),有多少個(gè)?綁定多少個(gè)參數(shù)值 preparedStatement.setString(1, userName); preparedStatement.setString(2, pwd); // 5、執(zhí)行SQL語(yǔ)句,并處理結(jié)果 ResultSet resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { System.out.println("用戶名和密碼正確,登錄成功"); } else { System.out.println("用戶名或密碼錯(cuò)誤,登錄失敗"); } // 6、釋放資源:與關(guān)閉流的方式一樣,先開(kāi)的后關(guān),后開(kāi)的先關(guān) resultSet.close(); preparedStatement.close(); connection.close(); sc.close(); } }
?PreparedStatement總結(jié)
PreparedStatement主要有如下的三個(gè)優(yōu)點(diǎn):
可以防止sql注入
由于使用了預(yù)編譯機(jī)制,執(zhí)行的效率要高于Statement
sql語(yǔ)句使用?形式替代參數(shù),然后再用方法設(shè)置?的值,比起拼接字符串,代碼更加優(yōu)雅.
PreparedStatement 與Statment比較:
語(yǔ)法不同:PreparedStatement可以使用預(yù)編譯的sql,而Statment只能使用靜態(tài)的sql
效率不同: PreparedStatement可以使用sql緩存區(qū),效率比Statment高
安全性不同: PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
??必須使用Statement的情況
當(dāng)sql語(yǔ)句中必須用到字符串拼接時(shí),則必須使用Statement
public static void main(String[] args) { Scanner s = new Scanner(System.in); System.out.print("升序輸入asc,降序輸入desc:"); String order = s.nextLine(); // 定義變量 Connection connection = null; Statement statement = null; ResultSet rs = null; try { // 注冊(cè)驅(qū)動(dòng) Class.forName("com.mysql.cj.jdbc.Driver"); // 獲取連接 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/MyTest","root", "********"); // 獲取數(shù)據(jù)庫(kù)操作對(duì)象 statement = connection.createStatement(); // 執(zhí)行sql String sql = "select * from emp order by sal " + order; rs = statement.executeQuery(sql); // 處理查詢結(jié)果集 while(rs.next()){ String ename = rs.getString("ename"); double sal = rs.getDouble("sal"); System.out.println("姓名:" + ename + ",薪資:" + sal); } } catch (ClassNotFoundException | SQLException e) { e.printStackTrace(); } finally { // 釋放資源 if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
到此這篇關(guān)于一文搞懂Java JDBC中的SQL注入問(wèn)題的文章就介紹到這了,更多相關(guān)jdbc sql 注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
WebSocket 中使用 @Autowired 注入對(duì)應(yīng)為null的解決方案
SpringBoot集成WebSocket時(shí),會(huì)遇到service對(duì)象為null的情況,原因是Spring默認(rèn)為單例模式與WebSocket的多對(duì)象模式相沖突,當(dāng)客戶端與服務(wù)器端建立連接時(shí),會(huì)創(chuàng)建新的WebSocket對(duì)象,本文給大家介紹WebSocket 中使用 @Autowired 注入對(duì)應(yīng)為null的問(wèn)題,感興趣的朋友一起看看吧2024-10-10Springboot詳解線程池與多線程及阻塞隊(duì)列的應(yīng)用詳解
本例應(yīng)用線程池、多線程、阻塞隊(duì)列處理一個(gè)流程任務(wù)。本例處理一個(gè)訂單流程,主要包括生成訂單、訂單處理、訂單入庫(kù),下面我們一起看看2022-06-06Java數(shù)據(jù)結(jié)構(gòu)之鏈表詳解
本篇文章我們將講解一種新型的數(shù)據(jù)結(jié)構(gòu)—鏈表,鏈表是一種使用廣泛的通用數(shù)據(jù)結(jié)構(gòu),它可以用來(lái)作為實(shí)現(xiàn)棧,隊(duì)列等數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ).文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-05-05五分鐘帶你了解Java的接口數(shù)據(jù)校驗(yàn)
這篇文章主要介紹了五分鐘帶你了解Java的接口數(shù)據(jù)校驗(yàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12Spring Boot中使用Actuator的/info端點(diǎn)輸出Git版本信息
這篇文章主要介紹了Spring Boot中使用Actuator的/info端點(diǎn)輸出Git版本信息,需要的朋友可以參考下2017-06-06java得到某年某周的第一天實(shí)現(xiàn)思路及代碼
某年某周的第一天,此功能如何使用java編程得到呢?既然有了問(wèn)題就有解決方法,感興趣的朋友可以了解下本文,或許會(huì)給你帶來(lái)意想不到的收獲哦2013-01-01