目錄
JDBC的簡單使用
向JDBC注入攻擊
防止注入攻擊
自建JDBC工具類
自建工具類優化--使用配置文件
使用數據庫連接池優化工具類
JDBC的簡單使用
1 package Test; 2 3 import java.sql.Connection; 4 import java.sql.Driver; 5 import java.sql.DriverManager; 6 import java.sql.DriverPropertyInfo; 7 import java.sql.ResultSet; 8 import java.sql.SQLException; 9 import java.sql.Statement; 10 11 12 /*JDBCsun公司提供的一套標准數據庫操作規范 13 * JDBC使用步驟 14 * 1 注冊驅動--告訴JVM使用的是哪一個數據庫 15 * 2 獲得連接--使用JDBC中的類完成對MySQL數據庫的連接 16 * 3 獲得語句執行平台--通過連接對象獲取SQL語句1的執行者對象 17 * 4 執行sql語句--使用執行者對象向數據庫執行SQL語句,並獲取執行后的結果 18 * 5 處理結果 19 * 6 釋放資源 20 * ----------在使用之前一定要先導入jar包 21 */ 22 23 public class Main{ 24 public static void main(String[] args) throws ClassNotFoundException, SQLException { 25 //1 注冊驅動,但查看源碼發現這樣會注冊兩次 26 //DriverManager.registerDriver(new Driver()); 27 //使用反射技術注冊,在這里類名可能找不到因此拋出類名查不到的異常 28 Class.forName("com.mysql.jdbc.Driver"); 29 30 //2連接數據庫 url:數據庫 jdbc:jdbc:mysql://連接主機IP:端口號//數據庫名 31 String url ="jdbc:mysql://localhost:3306/mybase"; 32 String username="root";//用戶名 33 String password="123";//密碼 34 //連接,可能連接不到拋出SQL異常 35 Connection con =DriverManager.getConnection(url, username,password); 36 37 //3 獲得語句執行平台,通過數據庫連接對象獲得SQL語句的執行者對象,注意導包為sql的包 38 Statement stat=con.createStatement(); 39 //查詢語句 40 String sql ="Select * from titles"; 41 42 //4 調用執行者對象方法,執行SQL語句獲取結果集 43 ResultSet rs=stat.executeQuery(sql); 44 //5 處理結果集 45 while(rs.next()){ 46 System.out.println(rs.getString("emp_no")+" "+rs.getString("title")); 47 } 48 49 //6 釋放資源 50 rs.close(); 51 stat.close(); 52 con.close(); 53 } 54 }
向JDBC注入攻擊
創建數據表
1 CREATE TABLE users( 2 username VARCHAR(20), 3 PASSWORD VARCHAR(10) 4 ); 5 6 INSERT INTO users VALUES('a','1'),('b','2');
待注入攻擊的代碼:
1 package Test; 2 3 import java.sql.Connection; 4 import java.sql.Driver; 5 import java.sql.DriverManager; 6 import java.sql.DriverPropertyInfo; 7 import java.sql.ResultSet; 8 import java.sql.SQLException; 9 import java.sql.Statement; 10 import java.util.Scanner; 11 12 13 /*MySQL注入攻擊 14 * 用戶登錄案例 15 */ 16 17 public class Main{ 18 public static void main(String[] args) throws ClassNotFoundException, SQLException { 19 Class.forName("com.mysql.jdbc.Driver"); 20 21 //2連接數據庫 url:數據庫 jdbc:jdbc:mysql://連接主機IP:端口號//數據庫名 22 String url ="jdbc:mysql://localhost:3306/mybase"; 23 String username="root";//用戶名 24 String password="123";//密碼 25 Connection con =DriverManager.getConnection(url, username,password); 26 Statement stat=con.createStatement(); 27 //查詢語句 28 Scanner sc=new Scanner(System.in); 29 String user=sc.next(); 30 String pass=sc.next(); 31 String sql ="Select * from users where username= '"+user+"' and password= '"+pass+"'"; 32 33 //4 調用執行者對象方法,執行SQL語句獲取結果集 34 ResultSet rs=stat.executeQuery(sql); 35 System.out.println(sql); 36 //5 處理結果集 37 while(rs.next()){ 38 System.out.println(rs.getString("username")+" "+rs.getString("password")); 39 } 40 41 //6 釋放資源 42 rs.close(); 43 stat.close(); 44 con.close(); 45 } 46 }
代碼運行結果:
攻擊的原理:
利用SQL語句:Select * from users where username= 'a' and password= '1'or'1=1',這樣由於最后一個是或運算那么就會顯示出來所有的數據,因此在輸入時只要想辦法湊成這樣的形式就可以了。
輸入:aa 12'or'1=1 這樣便可以完成攻擊,即使輸入的用戶名不對也可以正常登陸。
防止注入攻擊
1 package Test; 2 3 import java.sql.Connection; 4 import java.sql.Driver; 5 import java.sql.DriverManager; 6 import java.sql.DriverPropertyInfo; 7 import java.sql.PreparedStatement; 8 import java.sql.ResultSet; 9 import java.sql.SQLException; 10 import java.sql.Statement; 11 import java.util.Scanner; 12 13 /*MySQL防止注入攻擊--采用statement的子類preparedstatement 14 * 還可以用占位符實現增刪改查等操作 15 */ 16 17 public class Main{ 18 public static void main(String[] args) throws ClassNotFoundException, SQLException { 19 Class.forName("com.mysql.jdbc.Driver"); 20 21 //2連接數據庫 url:數據庫 jdbc:jdbc:mysql://連接主機IP:端口號//數據庫名 22 String url ="jdbc:mysql://localhost:3306/mybase"; 23 String username="root";//用戶名 24 String password="123";//密碼 25 Connection con =DriverManager.getConnection(url, username,password); 26 27 //查詢語句 28 Scanner sc=new Scanner(System.in); 29 String user=sc.next(); 30 String pass=sc.next(); 31 //用?占位符代替參數 32 String sql ="Select * from users where username=? and password= ?"; 33 PreparedStatement pds=con.prepareStatement(sql); 34 pds.setObject(1, user); 35 pds.setObject(2, pass); 36 //4 調用執行者對象方法,執行SQL語句獲取結果集 37 ResultSet rs=pds.executeQuery(); 38 System.out.println(sql); 39 //5 處理結果集 40 while(rs.next()){ 41 System.out.println(rs.getString("username")+" "+rs.getString("password")); 42 } 43 44 //6 釋放資源 45 rs.close(); 46 pds.close(); 47 con.close(); 48 } 49 }
自建JDBC工具類
JDBCUtils.class文件
1 package Test; 2 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import java.sql.Statement; 8 9 public class JDBCUtils { 10 private JDBCUtils(){} 11 private static Connection con; 12 13 static{ 14 try { 15 Class.forName("com.mysql.jdbc.Driver"); 16 //2連接數據庫 url:數據庫 jdbc:jdbc:mysql://連接主機IP:端口號//數據庫名 17 String url ="jdbc:mysql://localhost:3306/mybase"; 18 String username="root";//用戶名 19 String password="123";//密碼 20 con =DriverManager.getConnection(url, username,password); 21 } catch (Exception e) { 22 throw new RuntimeException(e+"數據庫連接失敗!"); 23 } 24 } 25 //定義靜態方法,返回數據庫的連接對象 26 public static Connection getConnection(){ 27 return con; 28 } 29 30 //釋放資源 31 public static void close(Connection con,Statement stat){ 32 if(stat!=null){ 33 try { 34 stat.close(); 35 } catch (SQLException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } 39 } 40 41 if(con!=null){ 42 try { 43 con.close(); 44 } catch (SQLException e) { 45 // TODO Auto-generated catch block 46 e.printStackTrace(); 47 } 48 } 49 } 50 //重載,關閉結果集 51 public static void close(Connection con,Statement stat,ResultSet rs){ 52 //注意釋放的順序 53 if(rs!=null){ 54 try { 55 rs.close(); 56 } catch (SQLException e) { 57 // TODO Auto-generated catch block 58 e.printStackTrace(); 59 } 60 } 61 62 if(stat!=null){ 63 try { 64 stat.close(); 65 } catch (SQLException e) { 66 // TODO Auto-generated catch block 67 e.printStackTrace(); 68 } 69 } 70 71 if(con!=null){ 72 try { 73 con.close(); 74 } catch (SQLException e) { 75 // TODO Auto-generated catch block 76 e.printStackTrace(); 77 } 78 } 79 } 80 }
測試代碼:
1 package Test; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import Test.JDBCUtils; 8 9 public class JDBCTset { 10 public static void main(String[] args) throws SQLException { 11 Connection con =JDBCUtils.getConnection(); 12 PreparedStatement pst=con.prepareStatement("SELECT * FROM titles"); 13 ResultSet rs =pst.executeQuery(); 14 while(rs.next()){ 15 System.out.println(rs.getString("title")); 16 } 17 //釋放資源 18 JDBCUtils.close(con, pst); 19 } 20 }
自建工具類優化--使用配置文件
優化代碼
1 package Test; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 6 /*在前面的代碼中由於數據庫用戶名,密碼直接在靜態代碼塊中,相當於寫死了代碼,不容易修改 7 * 因此采用properties配置文件,方便后期維護。配置文件建議放在src下,方便自動拷貝bin目錄下 8 * 9 */ 10 11 import java.sql.Connection; 12 import java.sql.DriverManager; 13 import java.sql.ResultSet; 14 import java.sql.SQLException; 15 import java.sql.Statement; 16 import java.util.Properties; 17 18 public class JDBCUtils { 19 private JDBCUtils(){} 20 private static Connection con; 21 private static String driverClass; 22 private static String url; 23 private static String username; 24 private static String password; 25 //放到靜態代碼塊中保證讀取配置文件,獲取連接只執行一次 26 static{ 27 try{ 28 readConfig(); 29 //反射 30 Class.forName(driverClass); 31 con=DriverManager.getConnection(url,username,password); 32 }catch(Exception e){ 33 throw new RuntimeException("數據庫連接失敗!"); 34 } 35 36 } 37 38 private static void readConfig() throws IOException{ 39 //通過字節流,使用類加載器讀取配置文件的內容 40 InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("database.properties"); 41 Properties pro = new Properties(); 42 pro.load(in); 43 driverClass=pro.getProperty("driverClass"); 44 url = pro.getProperty("url"); 45 username=pro.getProperty("username"); 46 password=pro.getProperty("password"); 47 } 48 49 //定義靜態方法,返回數據庫的連接對象 50 public static Connection getConnection(){ 51 return con; 52 } 53 54 //釋放資源 55 public static void close(Connection con,Statement stat){ 56 if(stat!=null){ 57 try { 58 stat.close(); 59 } catch (SQLException e) { 60 // TODO Auto-generated catch block 61 e.printStackTrace(); 62 } 63 } 64 65 if(con!=null){ 66 try { 67 con.close(); 68 } catch (SQLException e) { 69 // TODO Auto-generated catch block 70 e.printStackTrace(); 71 } 72 } 73 } 74 //重載,關閉結果集 75 public static void close(Connection con,Statement stat,ResultSet rs){ 76 //注意釋放的順序 77 if(rs!=null){ 78 try { 79 rs.close(); 80 } catch (SQLException e) { 81 // TODO Auto-generated catch block 82 e.printStackTrace(); 83 } 84 } 85 86 if(stat!=null){ 87 try { 88 stat.close(); 89 } catch (SQLException e) { 90 // TODO Auto-generated catch block 91 e.printStackTrace(); 92 } 93 } 94 95 if(con!=null){ 96 try { 97 con.close(); 98 } catch (SQLException e) { 99 // TODO Auto-generated catch block 100 e.printStackTrace(); 101 } 102 } 103 } 104 }
測試代碼
1 package Test; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.SQLException; 7 import Test.JDBCUtils; 8 9 public class JDBCTset { 10 public static void main(String[] args) throws SQLException { 11 Connection con =JDBCUtils.getConnection(); 12 //當能輸出數據庫連接時邊說用工具類正常了 13 System.out.println(con); 14 //由於是簡單的測試,因此不用釋放資源 15 //JDBCUtils.close(con, pst); 16 } 17 }
使用數據庫連接池優化工具類
優化代碼:
1 package Test; 2 import java.io.IOException; 3 import java.io.InputStream; 4 import java.util.Properties; 5 6 import javax.sql.DataSource; 7 8 /* 9 * JDBC連接池工具類,使用連接池專門負責數據庫的連接 10 * JDBC連接池簡化了sql語句中的增刪改查操作,方便代碼編寫 11 */ 12 import org.apache.commons.dbcp.BasicDataSource; 13 14 15 public class JDBCUtils { 16 private JDBCUtils(){} 17 private static String driverClass; 18 private static String url; 19 private static String username; 20 private static String password; 21 private static BasicDataSource datasource =new BasicDataSource(); 22 //放到靜態代碼塊中保證讀取配置文件,獲取連接只執行一次 23 static{ 24 try { 25 readConfig(); 26 //數據庫連接配置 27 datasource.setDriverClassName(driverClass); 28 datasource.setUrl(url); 29 datasource.setUsername(username); 30 datasource.setPassword(password); 31 //對象連接池中的數量配置,這些配置可以不用配置的 32 datasource.setInitialSize(10);//初始化的連接數 33 datasource.setMaxActive(8);//最大連接數 34 datasource.setMaxIdle(5);//最大空閑數 35 datasource.setMinIdle(1);//最小空閑數 36 } catch (IOException e) { 37 // TODO Auto-generated catch block 38 e.printStackTrace(); 39 } 40 } 41 42 private static void readConfig() throws IOException{ 43 //通過字節流,使用類加載器讀取配置文件的內容 44 InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("database.properties"); 45 Properties pro = new Properties(); 46 pro.load(in); 47 driverClass=pro.getProperty("driverClass"); 48 url = pro.getProperty("url"); 49 username=pro.getProperty("username"); 50 password=pro.getProperty("password"); 51 } 52 53 //定義靜態方法,返回數據庫的連接對象 54 public static DataSource getDataSource(){ 55 return datasource; 56 } 57 }
測試代碼:
1 package Test; 2 3 import java.sql.SQLException; 4 import java.util.List; 5 6 import org.apache.commons.dbutils.QueryRunner; 7 import org.apache.commons.dbutils.handlers.ArrayHandler; 8 import org.apache.commons.dbutils.handlers.ArrayListHandler; 9 10 public class JDBCTset { 11 private static QueryRunner qr=new QueryRunner(JDBCUtils.getDataSource()); 12 13 public static void main(String[] args) throws SQLException { 14 String sql="select * from titles"; 15 //ArrayListHandler:把結果集中的每一行數據都轉成一個對象數組,再存放到List中。這是dbutils所特有的 16 List<Object[]> list =qr.query(sql, new ArrayListHandler()); 17 for(Object[] objs:list){ 18 for(Object obj:objs) 19 System.out.print(obj+"\t\t"); 20 System.out.println(); 21 } 22 } 23 24 }
0