1.使用Statement实现用户登录界面连接数据库,存在SQL注入现象
package denglianbin.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
* 存在的问题:
请输入用户名:fdsa
请输入密码:fdsa' or '1'='1
登陆成功
这种现象被称为SQL注入
导致SQL注入的根本原因:用户输入的信息含有SQL语句的关键字,并且这些关键字参与了SQL语句的编译过程
导致原SQL语句的关键字被扭曲,进而达到SQL注入
*
* */
public class JDBCTest01 {
public static void main(String[] args) {
//初始化UI
Map<String, String> userLoginInfo = initUI();
//验证用户名和密码
boolean loginSuccess = login(userLoginInfo);
System.out.println(loginSuccess == true? "登陆成功" : "登陆失败");
}
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess = false;
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1.获取驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dlb_mysql",
"root","123456");
//3.获取数据库操作对象
stmt = conn.createStatement();
//4.执行SQL
String sql = "select * from t_user where loginName = '" + userLoginInfo.get("loginName") + "' and loginPwd =" +
"'"+ userLoginInfo.get("loginPwd") + "'";
//以上正好完成了SQL语句的拼接,以下代码的含义是发送SQL语句给DBMS,DBMS对SQL语句进行编译
//正好将非法的用户信息编译进去,导致原SQL语句的含义被扭曲
rs = stmt.executeQuery(sql);
//5.处理结果集
if(rs.next()){
loginSuccess = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6.释放资源
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt != null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loginSuccess;
}
private static Map<String,String> initUI() {
Scanner s = new Scanner(System.in);
System.out.print("请输入用户名:");
String loginName = s.nextLine();
System.out.print("请输入密码:");
String loginPwd = s.nextLine();
Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName", loginName);
userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo;
}
}
2.PreparedStatement可以解决SQL注入的现象
package denglianbin.jdbc;
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
* 1.解决SQL语句的注入问题:
* 只要用户提供的信息不参与SQL语句的注入过程,问题就解决了
* 即使用户提供的信息中含有SQL语句的关键字,也不起作用。
* 要想使用户信息不参与SQL语句的编译,就必须使用java.sql.PreparedStatement
* PreparedStatement接口继承了java.sql.statement
* PreparedStatement是属于预编译的数据库操作对象。
* PreparedStatement的原理是:预先对SQL语句的框架进行编译,再给SQL语句传值。
* 2.Statement和PreparedStatement的不同:*
* -Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题。
* -Statement编译一次,执行一次;PreparedStatement编译一次,执行n次,PreparedStatement较高。
* -PreparedStatement会在编译阶段做类型的安全检查。
* 故PreparedStatement使用的较多,只有极少数情况Statement草会用到。
*
*
*
*
* */
public class JDBCTest02 {
public static void main(String[] args) {
//初始化UI
Map<String, String> userLoginInfo = initUI();
//验证用户名和密码
boolean loginSuccess = login(userLoginInfo);
System.out.println(loginSuccess == true? "登陆成功" : "登陆失败");
}
private static boolean login(Map<String, String> userLoginInfo) {
boolean loginSuccess = false;
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.获取驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dlb_mysql",
"root","123456");
//3.获取预编译的数据库操作对象
//这里的?代表占位符,其中一个?代表一个占位符,一个?接受一个“值”,注意占位符不能使用单引号括起来
String sql = "select * from t_user where loginName = ? and loginPwd = ?";
ps = conn.prepareStatement(sql);//程序执行到这里,会发送SQL语句框架给DBMS,然后DBMS对SQL语句进行预编译
//给占位符传值,第一个下标为1,第二个下标为2,JDBC下表从1开始
ps.setString(1, userLoginInfo.get("loginName"));
ps.setString(2, userLoginInfo.get("loginPwd"));
//4.执行SQL
rs = ps.executeQuery();
//gei
//5.处理结果集
if(rs.next()){
loginSuccess = true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6.释放资源
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps != null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loginSuccess;
}
private static Map<String,String> initUI() {
Scanner s = new Scanner(System.in);
System.out.print("请输入用户名:");
String loginName = s.nextLine();
System.out.print("请输入密码:");
String loginPwd = s.nextLine();
Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName", loginName);
userLoginInfo.put("loginPwd", loginPwd);
return userLoginInfo;
}
}
3.区别:
-Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题。 -Statement编译一次,执行一次;PreparedStatement编译一次,执行n次,PreparedStatement较高。 -PreparedStatement会在编译阶段做类型的安全检查。 故PreparedStatement使用的较多,只有极少数情况Statement情况会用到。