“使用Statement的演示及其弊端”的版本间的差异
跳到导航
跳到搜索
Jihongchang(讨论 | 贡献) |
Jihongchang(讨论 | 贡献) |
||
(未显示同一用户的12个中间版本) | |||
第19行: | 第19行: | ||
* 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。 | * 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。 | ||
− | * Statement 接口中定义了下列方法用于执行 SQL 语句: | + | * Statement 接口中定义了下列方法用于执行 SQL 语句:<syntaxhighlight lang="java"> |
+ | package java.sql; | ||
+ | |||
+ | public interface Statement extends Wrapper, AutoCloseable { | ||
+ | ResultSet executeQuery(String sql) throws SQLException; | ||
+ | int executeUpdate(String sql) throws SQLException; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | *但是使用 Statement 操作数据库存在弊端: | ||
+ | **问题1:存在拼串操作,繁琐 | ||
+ | **问题2:存在 SQL 注入问题 | ||
+ | *SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,例如:<syntaxhighlight lang="java"> | ||
+ | public class Test6 { | ||
+ | |||
+ | public static void main(String[] args) { | ||
+ | |||
+ | String user = "zhangsan"; | ||
+ | |||
+ | String normalSQL = getStatementSQL(user); | ||
+ | |||
+ | System.out.println(normalSQL); | ||
+ | |||
+ | String injectParam = "zhangsan' or '1'='1"; | ||
+ | |||
+ | String injectSQL = getStatementSQL(injectParam); | ||
+ | |||
+ | System.out.println(injectSQL); | ||
+ | |||
+ | } | ||
+ | |||
+ | public static String getStatementSQL(String user) { | ||
+ | return "SELECT user, password FROM user_table WHERE user='" + user + "'"; | ||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | </syntaxhighlight><syntaxhighlight lang="console"> | ||
+ | SELECT user, password FROM user_table WHERE user='zhangsan' | ||
+ | SELECT user, password FROM user_table WHERE user='zhangsan' or '1'='1' | ||
+ | </syntaxhighlight> | ||
+ | *对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement (从 Statement 扩展而来)取代 Statement 就可以了。 | ||
+ | *存在 SQL 注入隐患的代码演示:<syntaxhighlight lang="java"> | ||
+ | import org.junit.Test; | ||
+ | |||
+ | import java.io.IOException; | ||
+ | import java.io.InputStream; | ||
+ | import java.lang.reflect.Field; | ||
+ | import java.sql.*; | ||
+ | import java.util.Properties; | ||
+ | import java.util.Scanner; | ||
+ | |||
+ | public class StatementTest { | ||
+ | |||
+ | @Test | ||
+ | public void testLogin() { | ||
+ | |||
+ | Scanner scanner = new Scanner(System.in); | ||
+ | |||
+ | System.out.print("请输入用户名:"); | ||
+ | |||
+ | String user = scanner.nextLine(); | ||
+ | |||
+ | System.out.print("请输入密码:"); | ||
+ | |||
+ | /* 当输入密码的字符串是: ' or '1' = '1 | ||
+ | * SQL 也会执行并返回数据 | ||
+ | * next() 和 nextLine() 的区别:空格也会被next()方法认为是结束输入的字符,所以这里要用 nextLine() | ||
+ | */ | ||
+ | String password = scanner.nextLine(); | ||
+ | |||
+ | String sql = "SELECT user, password from user_table where user = '" + user + "' and password = '" + password + "'"; | ||
+ | |||
+ | System.out.printf("\nsql:%s\n", sql); | ||
+ | |||
+ | User userInfo = get(sql, User.class); | ||
+ | |||
+ | if (userInfo != null) { | ||
+ | System.out.println("登录成功"); | ||
+ | } else { | ||
+ | System.out.println("用户名不存在或密码错误"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | } | ||
+ | </syntaxhighlight>https://github.com/jihch/jdbc/blob/main/src/main/java/io/github/jihch/statement/crud/StatementTest.java | ||
+ | |||
+ | |||
+ | === 解决方案 === | ||
+ | [[文件:注入Statement解决方案图1.png|无|缩略图|600x600像素]] |
2022年12月15日 (四) 03:39的最新版本
https://www.bilibili.com/video/BV1eJ411c7rf?p=12
操作和访问数据库
- 数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个 Socket 连接。
- 在 java.sql 包中有 3 个接口分别定义了对数据库的调用的不同方式:
- Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
- PreparedStatement:SQL 语句被编译并存储在此对象中,可以使用此对象多次高效地执行该语句。
- CallableStatement:用于执行 SQL 存储过程
使用 Statement 操作数据表的弊端
- 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。
- Statement 接口中定义了下列方法用于执行 SQL 语句:
package java.sql; public interface Statement extends Wrapper, AutoCloseable { ResultSet executeQuery(String sql) throws SQLException; int executeUpdate(String sql) throws SQLException; }
- 但是使用 Statement 操作数据库存在弊端:
- 问题1:存在拼串操作,繁琐
- 问题2:存在 SQL 注入问题
- SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令,例如:
public class Test6 { public static void main(String[] args) { String user = "zhangsan"; String normalSQL = getStatementSQL(user); System.out.println(normalSQL); String injectParam = "zhangsan' or '1'='1"; String injectSQL = getStatementSQL(injectParam); System.out.println(injectSQL); } public static String getStatementSQL(String user) { return "SELECT user, password FROM user_table WHERE user='" + user + "'"; } }
SELECT user, password FROM user_table WHERE user='zhangsan' SELECT user, password FROM user_table WHERE user='zhangsan' or '1'='1'
- 对于 Java 而言,要防范 SQL 注入,只要用 PreparedStatement (从 Statement 扩展而来)取代 Statement 就可以了。
- 存在 SQL 注入隐患的代码演示:https://github.com/jihch/jdbc/blob/main/src/main/java/io/github/jihch/statement/crud/StatementTest.java
import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.sql.*; import java.util.Properties; import java.util.Scanner; public class StatementTest { @Test public void testLogin() { Scanner scanner = new Scanner(System.in); System.out.print("请输入用户名:"); String user = scanner.nextLine(); System.out.print("请输入密码:"); /* 当输入密码的字符串是: ' or '1' = '1 * SQL 也会执行并返回数据 * next() 和 nextLine() 的区别:空格也会被next()方法认为是结束输入的字符,所以这里要用 nextLine() */ String password = scanner.nextLine(); String sql = "SELECT user, password from user_table where user = '" + user + "' and password = '" + password + "'"; System.out.printf("\nsql:%s\n", sql); User userInfo = get(sql, User.class); if (userInfo != null) { System.out.println("登录成功"); } else { System.out.println("用户名不存在或密码错误"); } } }