自学内容网 自学内容网

Jdbc学习笔记(四)--PreparedStatement对象、sql攻击(安全问题)

目录

(一)使用PreparedStatement对象的原因:

使用Statement对象编写sql语句会遇到的问题

​编辑 (二)sql攻击

1.什么是sql攻击

 2.演示sql攻击

 (三)防止SQL攻击

1.PreparedStatement是什么

2. PreparedStatement的使用

​编辑 3.总结:


(一)使用PreparedStatement对象的原因:

使用Statement对象编写sql语句会遇到的问题

使用Statement对象编写sql语句,都是拼接sql

问题:

  1. 可读性差
  2. 编写复杂,容易出错
  3. 安全问题,sql攻击(sql注入)  最大问题,系统会被攻破

 (二)sql攻击

1.什么是sql攻击

      在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!

 2.演示sql攻击

首先我们需要创建一张用户表,并插入数据,用来存储用户的信息

下面我们写一个login()方法!

public static boolean login(String username, String password) {
    Connection conn=null;
    Statement stmt=null;
    ResultSet rs=null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/esa?useSSL=false","root","123456");
        String sql="select count(1) num from user where username='"+username+"' and password='"+password+"'";
        System.out.println(sql);
        stmt=conn.createStatement();
        rs=stmt.executeQuery(sql);
        rs.next();
        int count=rs.getInt(1);
        return count >= 1;
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        try{
            if (rs!=null) rs.close();
            if (stmt!=null) stmt.close();
            if (conn!=null) conn.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

细节:

throw new RuntimeException(e);  不能写成e.printStackTrace(),不然会报错,无法运行

e.printStackTrace( )是打印异常栈信息

throw new RuntimeException(e)是把异常包在一个运行时异常中抛出。

因为return count >= 1,系统会无法识别return的是一个什么值,会显示缺少return语句;

下面是调用这个方法的代码:

login("a' or '1'='1", "b' or '1'='1"); 

控制台输出登陆成功,因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:

select count(1)  from tb_user where username = 'a' or '1'='1' and password='b' or '1'='1'

因为 'a' or '1 = 1'这个语句,or作为关键字只要有一边返回的是true那么这个整体的值就是true,右边始终返回true,所以不管左边是否正确,这个值永远是true,那么就很容易被别人登录进去,从而造成安全问题。

 (三)防止SQL攻击

jdk中提供一个Statement的子接口,java.sql.PreparedStatement

Statement在创建时不需要传sql语句,而是把sql语句拼接起来,发送给mysql服务器

PreparedStatement是预编译的Statement,可以使用占位符?,给值占位

1.PreparedStatement是什么

PreparedStatement叫预编译声明

什么是预编译:在sql语句执行之前,要先进行两个步骤,sql编译和sql语法分析,在语句第一遍执行完毕以后,PreparedStatement会将预编译结果保存下来,在一下遇到相同的sql语句时,就不需要再重新进行一遍这三个步骤,可以跳过前两个,这也是为什么效率提高了的原因

PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。

PreparedStatement的好处:

  • 防止SQL攻击;

  • 提高代码的可读性,以可维护性;

  • 提高效率。

2. PreparedStatement的使用

  1. 使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;

  2. 调用PreparedStatement的setXXX()系列方法为问号设置值

  3. 调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;

接下来我们来进行演示:

对之前的代码进行修改:

1.将拼接的username和password换成?

2.创建PreparedStatement对象,将Statement改成PreparedStatement

3.因为PreparedStatement的executeQuery()和executeUpdate()方法是无参的,所以要将括号中的sql删除

4.给?赋值

PreparedStatement提供了一组setXxx(int  问号的索引,值)方法,问号索引从1开始

如果不知道列的数据类型,有一个通用数据类型:setObject()

代码: 

public static boolean login2(String username, String password) {
    Connection conn=null;
    PreparedStatement stmt=null;
    ResultSet rs=null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/esa?useSSL=false","root","123456");
        String sql="select count(1) num from tb_user where username = ? and password= ?";
        System.out.println(sql);
        stmt = conn.prepareStatement(sql);
        //----------------
        //给?赋值
        //----------------
        stmt.setString(1,username);
        stmt.setString(2,password);
        rs=stmt.executeQuery();
        rs.next();
        int count=rs.getInt(1);
        return count >= 1;
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }catch (SQLException e) {
        throw new RuntimeException(e);
    } finally {
        try{
            if (rs != null) rs.close();
            if (stmt != null) stmt.close();
            if (conn != null) conn.close();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

执行结果:

登录失败!!,防止sql攻击成功

 3.总结:

 所以,建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement。  


原文地址:https://blog.csdn.net/2301_76890677/article/details/143771345

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!