7. 使用 preparedStatement 解决 SQL 注入问题

7. 使用 preparedStatement 解决 SQL 注入问题

前言

在上一章节中,我们使用 statement 执行 sql 完成了用户登录的小案例,但是在这个案例中也发现了 SQL 注入的问题。

而 SQL 注入的问题主要就是在字符串拼接中,存在查询条件拼接了 ' or '' = ' 后,导致可以查询所有数据的情况。

那么为了解决这个问题,我们就需要固化查询语句的结构,不允许随意拼接字符串。那么下面我们来介绍使用 preparedStatement 解决 SQL 注入问题。

案例-登录中SQL注入问题解决

1.目标

能够完成PreparedStatement改造登录案例, 解决SQL注入问题

2. preparedStatement概述

预编译SQL语句对象, 是Statement对象的子接口。

特点:

  • 性能要比Statement高

  • 会把sql语句先编译,格式固定好,

  • sql语句中的参数会发生变化,过滤掉用户输入的关键字(eg: or)

3. preparedStatement 用法

3.1 通过connection对象创建

  • connection.prepareStatement(String sql) ;创建prepareStatement对象

  • sql表示预编译的sql语句,如果sql语句有参数通过?来占位

    SELECT * FROM user WHERE username = ? AND password = ?
    

3.2 设置参数

  • prepareStatement.set类型(int i,Object obj);

    参数1 i 指的就是问号的索引(指第几个问号,从1开始),

    参数2就是值   eg:  setString(1,"zs");   setString(2,"123456");

4.使用 prepareStatement 来解决SQL注入

那么下面我们使用 prepareStatement 来解决SQL注入,修改登录案例的代码如下:

image-20210121082639304
public class LoginClient {

    /**
     * 使用 JDBC 实现登录案例
     */
    public static void main(String[] args) throws SQLException {

        //1. 获取用户输入的 用户名
        System.out.println("请输入的用户名: ");
        Scanner scanner = new Scanner(System.in);
        String username = scanner.nextLine();
        System.out.println("输入的用户名: " + username);

        //2. 获取数据库连接
        Connection connection = JdbcUtils.getConnection();

        //3. 根据用户名执行数据查询
        //String sql = "select * from t_user where uname = '" + username + "'"; // 设置SQL
        //Statement statement = connection.createStatement(); // 获取statement
        //ResultSet resultSet = statement.executeQuery(sql); // 执行查询SQL

        // 使用 prepareStatement 来解决SQL注入
        String sql = "select * from t_user where uname = ?"; // 设置SQL, 使用问号?设置查询的条件参数
        PreparedStatement preparedStatement = connection.prepareStatement(sql); // 获取preparedStatement
        // 设置SQL参数,执行SQL。
        preparedStatement.setString(1, username); // 设置用户名参数
        // 执行(还是executeQuery()和executeQUpdate(), 但是不需要再传入SQL语句, 上面已经传入了)
        ResultSet resultSet = preparedStatement.executeQuery();

        //4. 如果查询存在用户名的数据,那么则登录成功;反之,登录失败
        if (resultSet.next()) {
            System.out.println("登录成功");
        } else {
            System.out.println("登录失败");
        }
    }

}
  • 执行输入不存在的用户登录如下:

  • 拼接 ' or '' = ' 字符串,执行SQL注入如下:

可以看到,使用拼接字符串 ' or '' = ' 已经不能再次查询成功了,也就解决了 SQL注入问题。