前幾次有網友轉載我的博客也聲稱原創,我實在對這種人很無語耶,我轉載其他人的博客從來都是很尊重的,該是轉載的就寫明了轉載,雖然這裡沒有人去要求,但是這是對只是的一種尊重,對他人的尊重。人與人之間應如此,哪怕情侶之間也是如此(雖然我沒有談戀愛),但是在生活中我看到無論是小情侶還是小兩口,總存在我覺得不尊重對方的地方,這樣並不好。
言歸正傳,這節謝謝如何進行用戶名的驗證功能。
問題:
現去數據庫建個表,建好之後,結構如下圖:
好,本案例的詳細要求如下;
1.使用Statement實現用戶名和密碼的驗證功能,並測試用戶名為“wonderq”,密碼為"root"以及用戶名為"chenxin",密碼為“a' OR 'b'='b”是否能登錄成功。
2.使用PreparedStatement實現用戶和密碼的驗證功能,並測試用戶名為"chenxin",密碼為“a' OR 'b'='b”是否能登錄成功。
方案:
實現本案例的方案如下:
1.使用Statement實現用戶名和密碼的驗證功能。實例代碼如下:
sql="select * from users where username=' "+username+" ' " and password=' "+password+" ' "; System.out.println(sql); con=ConnectionSource.getConnection(); stmt=con.createStatement(); rs=stmr.executeQuery(sql);
上述代碼存在SQL注入的問題,可以使用PreparedStatement來解決SQL注入的問題。
2.使用PreparedStatement實現用戶名和密碼的驗證功能。preparedStatement是Statement的子類,表示預編譯的SQL語句的對象。在使用PreparedStatement對象執行SQL命令時,命令被數據庫編譯和解析,並放入命令緩沖區。緩沖區中的預編譯SQL命令可以重復使用。實例代碼如下所示:
sql="select * from users where username=? and password=?"; con=ConnectionSource.getConnection(); stmt=con.PrepareStatement(sql); stmt=setString(1,username); stmt=setString(2,password); rs=stmt.executeQuery();
使用PreparedStatement來執行SQL語句。在SQL語句中有2個問號,在代碼中要給它們設置值,從左到右,對應,1,2,....而且setxxx的xxx類型與變量類型要一致。
3.關於SQL注入。對於JDBC而言,SQL注入攻擊只對Statement有效,對PreparedStatement是無效的,這是因為PreparedStatement不允許在插入時改變查詢的邏輯結構。如果有一條SQL語句為:
"select * from 表 where 用戶名='用戶名' "
Statement的SQL語句是這樣寫的:
"select * from 表 where 用戶名=' "+變量值+" ' "
而PreparedStatement的SQL語句是這樣寫的:
"select * from 表 where 用戶名=?"
這樣輸入"aa 'or '1'='1' ",使用Statement執行的SQL語句為:
"select * from 表 where 用戶名=' aa 'or '1'='1' "
而使用PreparedStatement是將"' aa 'or '1'='1' "作為一個字符串復制給問號“?”,使其作為"用戶名"字段的對應值,這樣在防止SQL注入。
步驟:
實現本案例需要如下步驟:
步驟一:創建users表,並插入實例數據
在Mysql數據庫中,創建users表,並插入如下實例數據,SQL語句如下所示:
CREATE TABLE users( id NUMERIC(4), username VARCHAR(30), password VARCHAR(30) ); INSERT INTO users(id,username,password) VALUES (1,'wonderq','root'); INSERT INTO users(id,username,password) VALUES (2,'chenxin','sweet');
步驟二:使用Statement實現驗證用戶名密碼是否存在
新建UserDAO類,在該類中添加login方法,在該方法中使用Statement實現驗證用戶名密碼是否存在的方法,代碼如下所示:
package dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class UserDAO { public static void main(String[] args){ UserDAO dao=new UserDAO(); dao.login("wonderq", "root"); } public void login(String username,String password){ Connection con=null; Statement stmt=null; ResultSet rs=null; String sql=null; try { sql="select * from users where username='"+username+"'"+"and password="+"'"+password+"'"; System.out.println("執行的sql語句:"+sql); con=ConnectionSource.getConnection(); stmt=con.createStatement(); rs=stmt.executeQuery(sql); if(rs.next()){ System.out.println("登錄成功!"); }else{ System.out.println("登錄失敗!"); } } catch (SQLException e) { System.out.println("數據庫訪問異常!"); throw new RuntimeException(e); } finally{ try { if(rs!=null){ rs.close(); } if(stmt!=null){ stmt.close(); } if(con!=null){ con.close(); } } catch (SQLException e2) { System.out.println("釋放資源發生異常!"); } } } }
步驟三:使用用戶名為“wonderq”,密碼為"root"測試是否能登錄
只用在usersDAO類的主方法加入如下語句:
UserDAO dao=new UserDAO(); dao.login("wonderq", "root");運行usersDAO類,控制台輸出結果如下:
從輸入結果可以看出,使用用戶名為“wonderq”,密碼為“root”可以登錄成功,由於用戶名為"wonderq",密碼為"root"在數據庫中是存在的數據,登陸成功符合預期結果。另外,從輸出sql語句來看,最終變量信息替換為實際傳入的參數信息了。
步驟四:測試login方法
在UserDAO的main方法中,使用用戶名為"chenxin",密碼為"a' OR 'b'='b"作為login方法的參數,測試是否能成功登錄,代碼如下所示:
UserDAO dao=new UserDAO(); //dao.login("wonderq", "root"); dao.login("chenxin", "a' OR 'b'='b");運行UserDAO類,控制台輸出如下結果:
從輸出結果來看,使用用戶名為“chenxin”,密碼為"a' OR 'b'='b"可以登錄成功,但是用戶名為"chenxin",密碼為"a' OR 'b'='b"在數據庫中是不存在的數據,登錄成功不符合預期結果,問題出在哪裡呢?
問題出在sql語句上。查看上述控制台輸出結果,可以看出SQL語句的where字句中,使用or關鍵字連接兩個表達式,即or關鍵字後邊的表達式如果返回true。而or關鍵字後面的表達式為" 'b'='b" ,該表達式的返回結果永遠為true,因此,where條件的返回結果一定為true。這種現象在編程中成為SQL注入,是應該避免的編程方式。可以使用PreparedStatement來防止SQL注入。
步驟五:使用PreparedStatement實現驗證用戶名和密碼功能
在UserDAO類中更改login方法,在該方法中使用PreparedStatement執行SQL語句,來實現驗證用戶名密碼功能。代碼如下所示:
package dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class UserDAO { public static void main(String[] args){ UserDAO dao=new UserDAO(); //dao.login("wonderq", "root"); dao.login("chenxin", "a' OR 'b'='b"); } public void login(String username,String password){ Connection con=null; PreparedStatement stmt=null; ResultSet rs=null; String sql=null; try { sql="select * from users where username= ? and password= ?"; System.out.println("執行的sql語句:"+sql); con=ConnectionSource.getConnection(); stmt=con.prepareStatement(sql); stmt.setString(1, username); stmt.setString(2, password); rs=stmt.executeQuery(); if(rs.next()){ System.out.println("登錄成功!"); }else{ System.out.println("登錄失敗!"); } } catch (SQLException e) { System.out.println("數據庫訪問異常!"); throw new RuntimeException(e); } finally{ try { if(rs!=null){ rs.close(); } if(stmt!=null){ stmt.close(); } if(con!=null){ con.close(); } } catch (SQLException e2) { System.out.println("釋放資源發生異常!"); } } } }
步驟六:測試login方法
在UserDAO類的main方法中,使用用戶名為"chenxin",密碼為"a' OR 'b'='b"作為login方法的參數,測試是否能成功登錄,代碼就是上面的,運行之後,控制台輸出結果:
從輸出結果來看,登陸失敗。用戶名為"chenxin",密碼為"a' OR 'b'='b"的數據在數據庫users表中是不存在的數據,登錄失敗符合測試的預期,有效的防止了SQL注入的問題。
本節結束~。。下次進入JDBC的高級編程。。