[JAVA]IDEA演示使用PreparedStatement预编译接口防止SQL注入攻击

解决SQL注入问题

  • SQL注入攻击是指利用SQL漏洞越权获取数据的黑客行为
  • SQL注入攻击根源是未对原始SQL中的敏感字符做特殊处理
  • 解决方法:放弃Statement改用PreparedStatement处理SQL

那么什么是PreparedStatement?

PreparedStatement 预编译Statement是Statement的子接口,作为这个对象它可以对SQL进行参数化,预防SQL注入攻击,而且PreparedStatement比Statement执行效率更高

原始代码:

//存在SQL注入风险
//dno值为“ or 1=1 or 1=”时,所有筛选条件均失效
//SQL:select * from employee where pdname=" or 1=1 or 1="

String sql ="select * from employee where dname ='"+pdname+"'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){}
   ....
}

PreparedStatement改造以后的代码:

//利用PreparedStatement预防SQL注入风险
//当dname值为“or 1=1 or 1=”时,查询不到任何结果
//SQL:select * from employee where dname='\' or 1=1 or 1=\"
String sql ="select * from employee where dname=?";
PreparedStatement pstmt = conn.PreparedStatement(sql);
pstmt.setString(1,dname);//设置SQL参数,参数从1开始
ResultSet rs = pstmt.executeQuery();
while(rs.next()){
   ....
}

String sql = "select * from employee where dname=? ";

这段代码创建了一个SQL查询语句字符串,该语句的目的是从名为“employee”的表中查询所有满足特定部门名称(dname)条件的记录。其中“?”是一个占位符,用于后面设置具体的参数值

PreparedStatement pstmt = conn.PreparedStatement(sql);

这段代码使用数据库连接对象conn创建一个预编译的SQL语句对象pstmt,并传入前面定义的SQL查询语句。预编译的语句可以提高性能并且防止SQL注入攻击

pstmt.setString(1,dname);

这段代码将变量dname的值设置为SQL查询语句中第一个占位符的值。参数的索引从1开始,这是java中处理预编译语句参数的方式

ResultSet rs=pstmt.executeQuery();

这段代码执行预编译的SQL查询语句,并返回一个结果集rs,其中包含了满足查询条件的所有记录

while(rs.next()){

   ...... 

}

这个循环遍历结果集rs中的每一行记录。每次调用rs.next()会将游标移动到下一行记录,如果有下一行记录则返回true,否则返回false。在循环体中,可以通过rs的方法获取当前行各个列的值,并进行相应的处理

在IDEA代码中利用PreparedStatement接口实现查询部门信息,主要作用提供了一种更安全,更高效的方式来执行SQL查询,帮助防止SQL注入攻击。

首先我们定义一个接口Command,创建要用于连接数据库查询操作的execute方法

package hrapp;

public interface Command {
    public void execute();//执行的意思

}

在QueryCommand类中声明Command接口,并实现接口中的execute方法

package hrapp;

import java.sql.*;
import java.util.Scanner;

public class QueryCommand implements Command {

    @Override
    public void execute() {
        System.out.println("请输入部门名称:");
        Scanner in = new Scanner(System.in);
        String pdname = in.next();
        Connection conn = null;
        Statement stmt = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            //1.加载并注册JDBC驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.创建数据库连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/imooc?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true","root","123456");
            //3.创建PreparedStatement对象
            String sql = "select * from employee where dname=?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, pdname); //注意:参数索引从1
            //结果集
            rs = pstmt.executeQuery();
            //4.遍历查询结果
            /*rs.next()返回布尔值,代表是否存在下一条记录
             * 如果有,返回true,同时结果集提取下一条记录
             * 如果没有,返回false,循环就会停止
             * */
            while (rs.next()) {
                Integer eno = rs.getInt(1);//将当前数据的第一个字段的值提取出来转化为Int类型 JDBC中字段索引从1开始,而非0
                String ename = rs.getString("ename");//getString获取字符串
                Float salary = rs.getFloat("salary");
                String dname = rs.getString("dname");
                System.out.println(dname + "-" + eno + "-" + ename + "-" + salary);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //5.关闭连接,释放资源
            try {
                if (rs != null) {
                    rs.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            try {
                if (conn != null && !conn.isClosed()) {
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //5.关闭连接,释放资源
        }


    }
}
 

创建HumanResourceApplication类用于调用上述实现方法

package hrapp;

import com.im.jdbc.sample.hrapp.command.Command;
import com.im.jdbc.sample.hrapp.command.PstmtQueryCommand;
import com.im.jdbc.sample.hrapp.command.QueryCommand;

import java.util.Scanner; //导入了Scanner类 用于从用户输入中读取数据

public class HumanResourceApplication {
    public static void main(String[] args) {
        System.out.println("1-查询部门员工");
        Scanner in = new Scanner(System.in);
        Integer cmd = in.nextInt();
        //创建了一个Scanner对象in,用于从标准输入读取用户输入。然后,读取一个整数cmd,这个整数代表用户选择的命令
        switch (cmd) {
            case 1:   //查询部门员工
                Command command = new PstmtQueryCommand();
                command.execute();
                break;

        }
    }
}

然后我们对运行结果进行查询操作

输入要查询的部门名称

可以看到两名员工信息被正确打印出来

我们再进行测试SQL注入风险操作

 回车后,我们可以发现没有任何数据被打印

 作为PreparedStatement在传入参数之前,已经对原始的输入数据做了敏感字符的特殊处理,故查询不到数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值