✅作者简介:热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:乐趣国学的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:JAVA开发者成长之路
✨特色专栏:国学周更-心性养成之路
🥭本文内容:【JAVA高级】——吃透JDBC中的SQL注入问题和解决方案
更多内容点击👇
本文目录
💖SQL注入
✨什么是SQL注入
在用户输入的数据中有SQL关键字或语法,并且关键字或语法参与了SQL语句的编译。导致SQL语句编译后的条件为true,一直得到正确的结果。这种现象就是SQL注入。
✨SQL注入的效果的演示
💫SQL注入代码
-
package cn.bdqn.demo03;
-
-
import java.sql.Connection;
-
import java.sql.DriverManager;
-
import java.sql.ResultSet;
-
import java.sql.SQLException;
-
import java.sql.Statement;
-
import java.util.Scanner;
-
-
public
class
Login {
-
-
public
static
void
main
(String[] args)
throws ClassNotFoundException, SQLException {
-
-
//创建Scanner类对象,从控制台获取用户名和密码数据
-
Scanner
sc
=
new
Scanner(System.in);
-
System.out.println(
"请输入用户名:");
-
String
user
= sc.nextLine();
//使用nextLine()方法获取字符串
-
System.out.println(
"请输入密码:");
-
String
pwd
= sc.nextLine();
//使用nextLine()方法获取字符串
-
-
//1、注册驱动
-
Class.forName(
"com.mysql.jdbc.Driver");
-
//2、获取连接对象
-
String
url
=
"jdbc:mysql://127.0.0.1:3306/java221804";
-
String
dbuser
=
"root";
-
String
pssword
=
"123456";
-
Connection
connection
= DriverManager.getConnection(url, dbuser, pssword);
-
//3、获取发送SQL语句的对象
-
Statement
statement
=connection.createStatement();
-
//编写SQL语句
-
String
sql
=
"SELECT * FROM user WHERE username='"+user+
"' AND pssword = '"+pwd+
"';";
-
//4、执行SQL语句
-
ResultSet resultSet=statement.executeQuery(sql);
-
if(resultSet.next()){
-
System.out.println(
"用户名和密码正确,登录成功");
-
}
else{
-
System.out.println(
"用户名或密码不正确,登录失败");
-
}
-
//6、关闭资源
-
resultSet.close();
-
statement.close();
-
connection.close();
-
sc.close();
-
}
-
}
💫SQL注入效果
输入错误的用户名和密码,提示登录失败:
输入错误的用户名和密码,提示登录成功:产生了SQL注入
上面案例代码中,当你的用户名为 abc' or 1=1;# 密码为123,拼接到SQL语句中,变成如下效果:
SELECT * FROM user WHERE username='abc' or 1=1;#' AND pssword = '123';
此SQL语句or 后面1=1永远正确,#后面的成了注释,所以这条语句会将表中所有的数据查询出来,然后再做数据判断的时候,就会得到正确结果,从而说用户名和密码正确,登录成功。
✨如何避免SQL注入
使用PreparedStatement代替Statement可以有效防止SQL注入的发生。由于SQL注入产生的原因是在用户输入数据对SQL整合,整合后再发送到数据库进行编译产生的。
所以为了避免SQL注入,就需要SQL语句在用户输入数据前就进行编译,成为完整的SQL语句,编译完成后再进行数据填充。这个操作需要使用PrepareStatement实现。
PreparedStatement利用预编译的机制将sql语句的主干和参数分别传输给数据库服务器,从而使数据库分辨的出哪些是sql语句的主干哪些是参数,这样一来即使参数中带了sql的关键字,数据库服务器也仅仅将他当作参数值使用,关键字不会起作用,从而从原理上防止了sql注入的问题。
💖PrepareStatement解决SQL注入
PreparedStatement接口继承了Statement接口,执行SQL语句的方法与Statement执行SQL语句的方法相同。
✨PreparedStatement的应用
PreparedStatement的作用:
-
预编译SQL语句,效率高
-
安全,避免SQL注入
-
可以动态的填充数据,执行多个同结构的SQL语句
💫参数标记
//预编译SQL语句,SQL中的所有参数由?符号占位,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。
String sql = "select * from user where userName = ? and pssword=?;";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
💫动态参数绑定
preparedStatement.sexXxx(下标,值):参数下标从1开始,为指定参数下标绑定值。Xxx表示数据类型。
//绑定参数,有多少个?绑定多少个参数值
preparedStatement.setString(1, userName);
preparedStatement.setString(2, pwd);
✨综合案例
-
package cn.bdqn.demo02;
-
-
import java.sql.Connection;
-
import java.sql.DriverManager;
-
import java.sql.PreparedStatement;
-
import java.sql.ResultSet;
-
import java.sql.SQLException;
-
import java.util.Scanner;
-
-
public
class
PreparedStatementDemo01 {
-
-
public
static
void
main
(String[] args)
throws ClassNotFoundException,SQLException {
-
-
Scanner
sc
=
new
Scanner(System.in);
-
System.out.println(
"请输入用户名:");
-
String
userName
= sc.nextLine();
-
System.out.println(
"请输入密码:");
-
String
pwd
= sc.nextLine();
-
-
// 1、注册驱动
-
Class.forName(
"com.mysql.jdbc.Driver");
-
// 2、获得连接
-
String
url
=
"jdbc:mysql://localhost:3306/java2217";
-
String
user
=
"root";
-
String
pssword
=
"123456";
-
Connection
connection
= DriverManager.getConnection(url, user, pssword);
-
// 3、获取发送SQL对象
-
String
sql
=
"select * from user where userName = ? and pssword=?;";
-
PreparedStatement
preparedStatement
= connection.prepareStatement(sql);
-
// 4、绑定参数,有多少个?绑定多少个参数值
-
preparedStatement.setString(
1, userName);
-
preparedStatement.setString(
2, pwd);
-
// 5、执行SQL语句,并处理结果
-
ResultSet
resultSet
= preparedStatement.executeQuery();
-
if (resultSet.next()) {
-
System.out.println(
"用户名和密码正确,登录成功");
-
}
else {
-
System.out.println(
"用户名或密码错误,登录失败");
-
}
-
// 6、释放资源:与关闭流的方式一样,先开的后关,后开的先关
-
resultSet.close();
-
preparedStatement.close();
-
connection.close();
-
sc.close();
-
}
-
}
✨PreparedStatement总结
PreparedStatement主要有如下的三个优点:
可以防止sql注入
由于使用了预编译机制,执行的效率要高于Statement
sql语句使用?形式替代参数,然后再用方法设置?的值,比起拼接字符串,代码更加优雅.
PreparedStatement 与Statment比较:
语法不同:PreparedStatement可以使用预编译的sql,而Statment只能使用静态的sql
效率不同: PreparedStatement可以使用sql缓存区,效率比Statment高
安全性不同: PreparedStatement可以有效防止sql注入,而Statment不能防止sql注入。
💖必须使用Statement的情况
当sql语句中必须用到字符串拼接时,则必须使用Statement
-
public
static
void
main
(String[] args) {
-
Scanner
s
=
new
Scanner(System.in);
-
System.out.print(
"升序输入asc,降序输入desc:");
-
String
order
= s.nextLine();
-
// 定义变量
-
Connection
connection
=
null;
-
Statement
statement
=
null;
-
ResultSet
rs
=
null;
-
-
-
try {
-
// 注册驱动
-
Class.forName(
"com.mysql.cj.jdbc.Driver");
-
// 获取连接
-
connection = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/MyTest",
"root",
"********");
-
// 获取数据库操作对象
-
statement = connection.createStatement();
-
// 执行sql
-
String
sql
=
"select * from emp order by sal " + order;
-
rs = statement.executeQuery(sql);
-
// 处理查询结果集
-
while(rs.next()){
-
String
ename
= rs.getString(
"ename");
-
double
sal
= rs.getDouble(
"sal");
-
System.out.println(
"姓名:" + ename +
",薪资:" + sal);
-
}
-
}
catch (ClassNotFoundException | SQLException e) {
-
e.printStackTrace();
-
}
finally {
-
// 释放资源
-
if (rs !=
null) {
-
try {
-
rs.close();
-
}
catch (SQLException e) {
-
e.printStackTrace();
-
}
-
}
-
if (statement !=
null) {
-
try {
-
statement.close();
-
}
catch (SQLException e) {
-
e.printStackTrace();
-
}
-
}
-
if (connection !=
null) {
-
try {
-
connection.close();
-
}
catch (SQLException e) {
-
e.printStackTrace();
-
}
-
}
-
}
-
}
💖投票传送门(欢迎伙伴们投票)
转载:https://blog.csdn.net/hh867308122/article/details/127539419