“使用Statement的演示及其弊端”的版本间的差异

来自姬鸿昌的知识库
跳到导航 跳到搜索
 
(未显示同一用户的14个中间版本)
第10行: 第10行:
 
** CallableStatement:用于执行 SQL 存储过程
 
** CallableStatement:用于执行 SQL 存储过程
 
[[文件:操作和访问数据库 图1.png|无|缩略图|600x600像素]]
 
[[文件:操作和访问数据库 图1.png|无|缩略图|600x600像素]]
 +
 +
 +
 +
 +
 +
=== 使用 Statement 操作数据表的弊端 ===
 +
 +
* 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 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 存储过程
操作和访问数据库 图1.png



使用 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 注入隐患的代码演示:
    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("用户名不存在或密码错误");
            }
        }
    
    }
    
    https://github.com/jihch/jdbc/blob/main/src/main/java/io/github/jihch/statement/crud/StatementTest.java


解决方案

注入Statement解决方案图1.png