一文搞懂Java?JDBC中的SQL注入問題

??SQL注入
?什么是SQL注入
在用戶輸入的數(shù)據(jù)中有SQL關(guān)鍵字或語法,并且關(guān)鍵字或語法參與了SQL語句的編譯。導(dǎo)致SQL語句編譯后的條件為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類對象,從控制臺獲取用戶名和密碼數(shù)據(jù)
Scanner sc = new Scanner(System.in);
System.out.println("請輸入用戶名:");
String user = sc.nextLine();//使用nextLine()方法獲取字符串
System.out.println("請輸入密碼:");
String pwd = sc.nextLine();//使用nextLine()方法獲取字符串
//1、注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
//2、獲取連接對象
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語句的對象
Statement statement =connection.createStatement();
//編寫SQL語句
String sql = "SELECT * FROM user WHERE username='"+user+"' AND pssword = '"+pwd+"';";
//4、執(zhí)行SQL語句
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注入效果
輸入錯誤的用戶名和密碼,提示登錄失?。?/p>

輸入錯誤的用戶名和密碼,提示登錄成功:產(chǎn)生了SQL注入

上面案例代碼中,當(dāng)你的用戶名為 abc' or 1=1;# 密碼為123,拼接到SQL語句中,變成如下效果:
SELECT * FROM user WHERE username='abc' or 1=1;#' AND pssword = '123';
此SQL語句or 后面1=1永遠(yuǎn)正確,#后面的成了注釋,所以這條語句會將表中所有的數(shù)據(jù)查詢出來,然后再做數(shù)據(jù)判斷的時候,就會得到正確結(jié)果,從而說用戶名和密碼正確,登錄成功。
?如何避免SQL注入
使用PreparedStatement代替Statement可以有效防止SQL注入的發(fā)生。由于SQL注入產(chǎn)生的原因是在用戶輸入數(shù)據(jù)對SQL整合,整合后再發(fā)送到數(shù)據(jù)庫進(jìn)行編譯產(chǎn)生的。
所以為了避免SQL注入,就需要SQL語句在用戶輸入數(shù)據(jù)前就進(jìn)行編譯,成為完整的SQL語句,編譯完成后再進(jìn)行數(shù)據(jù)填充。這個操作需要使用PrepareStatement實現(xiàn)。
PreparedStatement利用預(yù)編譯的機(jī)制將sql語句的主干和參數(shù)分別傳輸給數(shù)據(jù)庫服務(wù)器,從而使數(shù)據(jù)庫分辨的出哪些是sql語句的主干哪些是參數(shù),這樣一來即使參數(shù)中帶了sql的關(guān)鍵字,數(shù)據(jù)庫服務(wù)器也僅僅將他當(dāng)作參數(shù)值使用,關(guān)鍵字不會起作用,從而從原理上防止了sql注入的問題。
??PrepareStatement解決SQL注入
PreparedStatement接口繼承了Statement接口,執(zhí)行SQL語句的方法與Statement執(zhí)行SQL語句的方法相同。
?PreparedStatement的應(yīng)用
PreparedStatement的作用:
- 預(yù)編譯SQL語句,效率高
- 安全,避免SQL注入
- 可以動態(tài)的填充數(shù)據(jù),執(zhí)行多個同結(jié)構(gòu)的SQL語句
??參數(shù)標(biāo)記
//預(yù)編譯SQL語句,SQL中的所有參數(shù)由?符號占位,這被稱為參數(shù)標(biāo)記。在執(zhí)行SQL語句之前,必須為每個參數(shù)提供值。
String sql = "select * from user where userName = ? and pssword=?;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
??動態(tài)參數(shù)綁定
preparedStatement.sexXxx(下標(biāo),值):參數(shù)下標(biāo)從1開始,為指定參數(shù)下標(biāo)綁定值。Xxx表示數(shù)據(jù)類型。
//綁定參數(shù),有多少個?綁定多少個參數(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("請輸入用戶名:");
String userName = sc.nextLine();
System.out.println("請輸入密碼:");
String pwd = sc.nextLine();
?
// 1、注冊驅(qū)動
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對象
String sql = "select * from user where userName = ? and pssword=?;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 4、綁定參數(shù),有多少個?綁定多少個參數(shù)值
preparedStatement.setString(1, userName);
preparedStatement.setString(2, pwd);
// 5、執(zhí)行SQL語句,并處理結(jié)果
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
System.out.println("用戶名和密碼正確,登錄成功");
} else {
System.out.println("用戶名或密碼錯誤,登錄失敗");
}
// 6、釋放資源:與關(guān)閉流的方式一樣,先開的后關(guān),后開的先關(guān)
resultSet.close();
preparedStatement.close();
connection.close();
sc.close();
}
}?PreparedStatement總結(jié)
PreparedStatement主要有如下的三個優(yōu)點:
可以防止sql注入
由于使用了預(yù)編譯機(jī)制,執(zhí)行的效率要高于Statement
sql語句使用?形式替代參數(shù),然后再用方法設(shè)置?的值,比起拼接字符串,代碼更加優(yōu)雅.
PreparedStatement 與Statment比較:
語法不同:PreparedStatement可以使用預(yù)編譯的sql,而Statment只能使用靜態(tài)的sql
效率不同: PreparedStatement可以使用sql緩存區(qū),效率比Statment高
安全性不同: PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
??必須使用Statement的情況
當(dāng)sql語句中必須用到字符串拼接時,則必須使用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 {
// 注冊驅(qū)動
Class.forName("com.mysql.cj.jdbc.Driver");
// 獲取連接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/MyTest","root", "********");
// 獲取數(shù)據(jù)庫操作對象
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注入問題的文章就介紹到這了,更多相關(guān)jdbc sql 注入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
WebSocket 中使用 @Autowired 注入對應(yīng)為null的解決方案
SpringBoot集成WebSocket時,會遇到service對象為null的情況,原因是Spring默認(rèn)為單例模式與WebSocket的多對象模式相沖突,當(dāng)客戶端與服務(wù)器端建立連接時,會創(chuàng)建新的WebSocket對象,本文給大家介紹WebSocket 中使用 @Autowired 注入對應(yīng)為null的問題,感興趣的朋友一起看看吧2024-10-10
Springboot詳解線程池與多線程及阻塞隊列的應(yīng)用詳解
本例應(yīng)用線程池、多線程、阻塞隊列處理一個流程任務(wù)。本例處理一個訂單流程,主要包括生成訂單、訂單處理、訂單入庫,下面我們一起看看2022-06-06
Java數(shù)據(jù)結(jié)構(gòu)之鏈表詳解
本篇文章我們將講解一種新型的數(shù)據(jù)結(jié)構(gòu)—鏈表,鏈表是一種使用廣泛的通用數(shù)據(jù)結(jié)構(gòu),它可以用來作為實現(xiàn)棧,隊列等數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ).文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-05-05
Spring Boot中使用Actuator的/info端點輸出Git版本信息
這篇文章主要介紹了Spring Boot中使用Actuator的/info端點輸出Git版本信息,需要的朋友可以參考下2017-06-06

