首先我們模擬下簡單的登錄功能:
簡單需求:
模擬用戶登錄的簡單實現
業務描述:
程序運行 的時候,提供一個輸入的人口,可以使用輸入用戶名個密碼,校驗用戶名和密碼是否合法,數據庫中是否存在用戶
合法登錄成功,否則注冊登錄
數據准備:
設計數據庫表,通常我們使用建模工具:PowerDesigner
接下來上代碼:
db.properties配置文件如下:
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/user user=root password=guisha
實現如下:
package com.guisha.JDBC.Logn;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
public class JDBCTest {
public static void main(String[] agre) {
//獲取登錄時的用戶和密碼
Map<String, String> loginInfo = loginInfo();
//獲取用戶名登錄驗證
boolean loginSuccess = login(loginInfo);
//輸出結果驗證
System.out.println(loginSuccess ? "登錄成功!" : "請注冊登錄!");
}
private static boolean login(Map<String, String> loginInfo) {
//登錄標記
boolean flag = false;
//定義用戶名和登錄密碼
String loginName = loginInfo.get("loginName");
String loginPwa = loginInfo.get("loginPwa");
//使用的接口
Connection conn = null;
Statement stan = null;
ResultSet result = null;
try {
InputStream input = JDBCTest.class.getClassLoader().getResourceAsStream("db.properties");
Properties property = new Properties();
try {
//裝載配置文件
property.load(input);
String driver = property.getProperty("driver");
String url = property.getProperty("url");
String user = property.getProperty("user");
String password = property.getProperty("password");
//注冊驅動
Class.forName(driver);
//從輸入字節流中讀取屬性列表(鍵和元素對)。輸入流采用load(Reader)中指定的簡單的面向行的格式,並假定使用ISO 8859-1字符編碼。
try {
//創建鏈接
conn = DriverManager.getConnection(url, user, password);
//獲取數據庫操作對象
stan = conn.createStatement();
//執行Sql,獲取數據
String sql = "select * from loginname where NAME='" + loginName + "' and NAMEPASS= '" + loginPwa + "' ";
// 查詢結果集
result = stan.executeQuery(sql);
//判斷
if (result.next()){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}finally {
//釋放資源
if (result != null){
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//釋放資源
if (stan != null){
try {
stan.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//釋放資源
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//返回標識
return flag;
}
public static Map<String, String> loginInfo() {
//接受鍵盤輸入
Scanner scanner = new Scanner(System.in);
//輸入用戶名
System.out.println("請輸入用戶名:");
String loginName = scanner.nextLine();
//輸入密碼
System.out.println("請輸入密碼:");
String loginPwa = scanner.nextLine();
//創建鍵值對集合
Map<String, String> map = new HashMap<String, String>();
//將數據添加到map集合對象中
map.put("loginName", loginName);
map.put("loginPwa", loginPwa);
//返回集合對象
return map;
}
}
我們需要了解SQL注入

以上代碼中執行執行SQL部分,這里完成了SQL語句的拼接,而executeQuery這里是將sql語句發送給DBMS進行代碼編譯
正好將用戶提供的"非法信息"編譯進去,導致原SQL語句的含義被扭曲,會導不正常的操作變的正常、
導致SQL注入的根本原因是什么?
用戶輸入的信息中包含SQL語句的關鍵字,並且這些關鍵字參與sql語句的編譯過程,導致SQL語句原意改變,進而達到SQL注入的效果(不能小視,可能會造成數據庫崩潰或者被改變)
解決SQL注入問題?
首先我們知道出現SQL注入的原因是用戶在提供的信息包含SQL語句的關鍵字,並且在傳給DBMS預編譯時傳入了非法的SQL信息,那么,只要用戶提供的信息不參與SQL語句的編譯過程,就可以解決問題(即使信息中包含SQL語句的關鍵字,但是不參與預編譯,不起作用)。
想要用戶信息不參與SQL預編譯,就需要使用java.sql.PreparedStatement接口,此接口繼承了java.sql.Statement。
PreparedStatement是屬於預編譯的數據庫操作對象,原理是:預先對SQL語句的框架進行預編譯,然后再給SQL語句傳值
我們對代碼進行修改:
package com.guisha.JDBC.Logn;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
public class JDBCTest {
public static void main(String[] agre) {
//獲取登錄時的用戶和密碼
Map<String, String> loginInfo = loginInfo();
//獲取用戶名登錄驗證
boolean loginSuccess = login(loginInfo);
//輸出結果驗證
System.out.println(loginSuccess ? "登錄成功!" : "請注冊登錄!");
}
private static boolean login(Map<String, String> loginInfo) {
//登錄標記
boolean flag = false;
//定義用戶名和登錄密碼
String loginName = loginInfo.get("loginName");
String loginPwa = loginInfo.get("loginPwa");
//使用的接口
Connection conn = null;
PreparedStatement ps = null;
ResultSet result = null;
try {
InputStream input = JDBCTest.class.getClassLoader().getResourceAsStream("db.properties");
Properties property = new Properties();
try {
//裝載配置文件
property.load(input);
String driver = property.getProperty("driver");
String url = property.getProperty("url");
String user = property.getProperty("user");
String password = property.getProperty("password");
//注冊驅動
Class.forName(driver);
//從輸入字節流中讀取屬性列表(鍵和元素對)。輸入流采用load(Reader)中指定的簡單的面向行的格式,並假定使用ISO 8859-1字符編碼。
try {
//創建鏈接
conn = DriverManager.getConnection(url, user, password);
//獲取數據庫操作對象
//SQL語句,其中一個 ? ,表示一個占位符,一個?將來接受一個“值”,注意:占位符不能使用 ' ' 括起來
String sql = "select * from loginname where NAME= ? and NAMEPASS = ? ";
ps= conn.prepareStatement(sql);
//給占位符串 “值” (第一個? 下標為1,第二個為2,切記 JDBC中所有的下標從1開始)
ps.setString(1,loginName);
ps.setString(2,loginPwa);
//執行Sql,獲取數據對語句進行預編譯
// 獲取結果集
result = ps.executeQuery();
//判斷
if (result.next()){
flag = true;
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}finally {
//釋放資源
if (result != null){
try {
result.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//釋放資源
if (ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//釋放資源
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//返回標識
return flag;
}
public static Map<String, String> loginInfo() {
//接受鍵盤輸入
Scanner scanner = new Scanner(System.in);
//輸入用戶名
System.out.println("請輸入用戶名:");
String loginName = scanner.nextLine();
//輸入密碼
System.out.println("請輸入密碼:");
String loginPwa = scanner.nextLine();
//創建鍵值對集合
Map<String, String> map = new HashMap<String, String>();
//將數據添加到map集合對象中
map.put("loginName", loginName);
map.put("loginPwa", loginPwa);
//返回集合對象
return map;
}
}
