JDBC中存在的SQL注入问题及Statement对象和PreparedStatement对象的区别
JDBC中存在的SQL注入问题
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
实现功能:
1、需求:模拟用户登录功能的实现。
2、业务描述:
程序运行的时候,提供一个输入的入口,然用户输入用户名和密码
用户输入用户名和密码之后,提交信息,java程序收集用户信息
java线程连接数据库验证用户名和密码是否合法
合法:显示登录成功
不合法:显示登录失败
参见user_login.sql脚本
3、sql注入(安全隐患)
请输入用户名:fdsa
请输入密码:fdsa'or'1'='1
登录成功
4、导致SQL注入的根本原因是什么?
用户输入的信息中有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程
导致SQL语句原意被扭曲,进而达到SQL注入。
*/
public class JDBCTest06 {
public static void main(String[] args) {
// 1.初始化界面
Map<String,String> userLoginInfo = initUI();
// 2.验证用户名密码
boolean loginSuccess = login(userLoginInfo);
// 3.输出结果
System.out.println(loginSuccess ? "登录成功" : "登录失败");
//System.out.println(in_user + "," + in_password);
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false 登录失败 true 登录成功
*/
private static boolean login(Map<String, String> userLoginInfo) {
// 打标记
boolean loginSuccess = false;
// 获取输入的用户名密码
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
// JDBC代码
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
// 注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
// 获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root", "123");
// 获取数据库操作对象
statement = connection.createStatement();
// 执行sql语句
resultSet = statement.executeQuery("select * from t_user where loginName='" + loginName + "' and loginPwd='"+ loginPwd +"'");
// 处理查询结果集
if (resultSet.next()){
loginSuccess = true;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 释放资源
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面
* @return 用户输入的用户名和密码等登录信息
*/
private static Map<String,String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String loginName = scanner.next();
System.out.print("请输入密码:");
String loginPwd = scanner.next();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
JDBC中解决SQL注入
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
1、解决SQL注入问题
只要用户提供的信息不参与SQL语句的编译过程问题就解决了
即使用户提供的信息中含有SQL语句的关键字,但是不参与编译
就不起作用,那么就必须使用java.sql.PreparedStatement
PreparedStatement接口继承了java.sql.Statement
PreparedStatement是属于预编译的操作对象
PreparedStatement原理是先对SQL语句框架进行编译,然后再给SQL语句传“值”
2、解决SQL注入的关键是什么?
用户提供的信息中即使含有SQL语句的关键字,但是这些关键字不参与编译,不起作用。
3、比较Statement和PreparedStatement
-Statement存在SQL注入问题,PreparedStatement解决了SQL注入问题
-Statement编译一次执行一次,PreparedStatement编译一次可执行n次,PreparedStatement效率更高
—PreparedStatement会在编译阶段做类型的安全检查
综上所述,PreparedStatement使用较多,Statement使用极少
4、什么情况下必须使用Statement呢?
业务方面要求必须支持SQL注入时
Statement支持SQL注入,业务方面要求进行SQL拼接的,必须使用Statement
*/
public class JDBCTest07 {
public static void main(String[] args) {
// 1.初始化界面
Map<String,String> userLoginInfo = initUI();
// 2.验证用户名密码
boolean loginSuccess = login(userLoginInfo);
// 3.输出结果
System.out.println(loginSuccess ? "登录成功" : "登录失败");
//System.out.println(in_user + "," + in_password);
}
/**
* 用户登录
* @param userLoginInfo 用户登录信息
* @return false 登录失败 true 登录成功
*/
private static boolean login(Map<String, String> userLoginInfo) {
// 打标记
boolean loginSuccess = false;
// 获取输入的用户名密码
String loginName = userLoginInfo.get("loginName");
String loginPwd = userLoginInfo.get("loginPwd");
// JDBC代码
Connection connection = null;
PreparedStatement preparedStatement = null; // 使用预编译的数据库操作对象
ResultSet resultSet = null;
// 1、注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
// 2、获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root", "123");
// 3、获取预编译的数据库操作对象
// 一个?代表一个占位符,将来一个?接收一个“值”,占位符不能使用单引号括起来
String sql = "select * from t_user where loginName=? and loginPwd=?";
// 程序执行到这里,会发送SQL语句框子给DBMS,然后DBMS进行SQL语句的预编译。
preparedStatement = connection.prepareStatement(sql);
// 给占位符?传值(第一个问号下标是1,第二个问号下标是2,JDBC中所有下标从1开始)
preparedStatement.setString(1,loginName);
preparedStatement.setString(2,loginPwd);
// 4、执行sql语句
resultSet = preparedStatement.executeQuery();
// 5、处理查询结果集
if (resultSet.next()){
loginSuccess = true;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
// 6、释放资源
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化用户界面
* @return 用户输入的用户名和密码等登录信息
*/
private static Map<String,String> initUI() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String loginName = scanner.next();
System.out.print("请输入密码:");
String loginPwd = scanner.next();
Map<String,String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
return userLoginInfo;
}
}
必须使用Statement对象的情况
import java.sql.*;
import java.util.ArrayList;
import java.util.Scanner;
/*
用户在控制台输入desc就是降序,输入asc就是升序
*/
public class JDBCTest08 {
public static void main(String[] args) {
// 初始化用户界面,接收用户输入
String info = initUI();
// 执行SQL
ArrayList<String> arrayLists = excuteDQL(info);
// 显示查询结果
show(arrayLists);
}
private static void show(ArrayList<String> arrayLists) {
for (String list : arrayLists) {
System.out.println(list);
}
}
private static ArrayList<String> excuteDQL(String info) {
ArrayList<String> resultLists = new ArrayList<>();
//JDBC编程
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
// 注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
// 获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123");
// 获取数据库操作对象
statement = connection.createStatement();
// 执行SQL语句
String sql = "select empno,ename,sal from emp order by sal " + info;
resultSet = statement.executeQuery(sql);
// 处理查询结果集
while (resultSet.next()) {
String empno = resultSet.getString("empno");
String ename = resultSet.getString("ename");
String sal = resultSet.getString("sal");
resultLists.add(empno + "," + ename + "," + sal);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//释放资源
if (resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return resultLists;
}
private static String initUI() {
Scanner scanner = new Scanner(System.in);
System.out.println("升序输入ASC,降序输入DESC");
String info = scanner.next();
return info;
}
}
使用PreparedStatement完成DML操作
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/*
PreparedStatement完成DML操作(insert delete update)
*/
public class JDBCTest09 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
/*
//PreparedStatement完成insert操作
//2.获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123");
//3.获取预编译的数据库操作对象
String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,50);
preparedStatement.setString(2,"销售部");
preparedStatement.setString(3,"shanghai");
//4.执行SQL语句
int count = preparedStatement.executeUpdate();
System.out.println(count == 1 ? "执行成功" : "执行失败");
*/
/*
//PreparedStatement完成update操作
//2.获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123");
//3.获取预编译的数据库操作对象
String sql = "update dept set dname=?,loc=? where deptno=?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1,"宣传部");
preparedStatement.setString(2,"北京");
preparedStatement.setInt(3,50);
//4.执行SQL语句
int count = preparedStatement.executeUpdate();
System.out.println(count == 1 ? "执行成功" : "执行失败");
*/
//PreparedStatement完成delete操作
//2.获取连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123");
//3.获取预编译的数据库操作对象
String sql = "delete from dept where deptno=?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,50);
//4.执行SQL语句
int count = preparedStatement.executeUpdate();
System.out.println(count == 1 ? "执行成功" : "执行失败");
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//6.释放资源
if (preparedStatement != null) {
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}