JDBC3——SQL注入、及其解决方法——Statement与PreparedStatement对比——PreparedStatement的CRUD

1.用户登陆系统演示—Statement—SQL注入

1.1.需求:
  •  模拟用户登陆功能的实现
    
  • 用户名:zhangsan
  • 密码:123
1.2.业务描述:
  •  程序运行的时候,提供一个输入的入口,可以让用户输入用户名、密码
     输入后,提交信息,java程序搜集到用户信息
    
  •  JAVA程序连接数据库验证是否合法
     	 合法:显示登陆成功
     	 非法:显示登陆失败
    
1.3.本程序存在的问题—— 存在SQL语句注入现象
  • 用户名:a
  • 密码:b’ or ‘1’='1
  • 会登录成功
1.4.导致SQL语句注入的根本原因:
  • 用户输入的东西,含有sql关键字,而且编译进去了,导致系统原来的意思被扭曲!!!!
1.5.解决方法:
  • 只要用户输入的SQL关键字不编译进去就可以了
    1.6.执行结果
  • 正确实例:
    在这里插入图片描述
  • 错误结果:
    在这里插入图片描述
public class JDBCTestSqlInject {
    public static void main(String[] args) {
        //1.输入信息
        Map<String, String> userLoginInfo = inintUI();

        //2.验证信息是否正确
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess ? "登陆成功" : "登陆失败");

    }
        /**
         * 验证用户名,密码是否正确
         * @param userLoginInfo
         * @return
         */
        private static boolean login (Map < String, String > userLoginInfo){
            boolean flag=false;
            String  loginName = userLoginInfo.get("loginName");
            String  loginPwd = userLoginInfo.get("loginPwd");
            //JDBC代码
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;


            try {
                //1.注册驱动(作用:告诉java程序,即将要连接哪个品牌的数据库)
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取连接(表示JVM的进程和数据库进程之间的  通道 打开了,使用完之后  必须关闭)
                String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址链接
                String user = "root";
                String password = "333";
                conn = DriverManager.getConnection(url,user,password);
                //3.获取数据库操作对象(专门执行sql语句的对象)
                stmt = conn.createStatement();
                //4.执行sql语句(主要执行DQL、DML……)
                String sql = " select * from t_user where loginName ='"+ loginName +"'and loginPwd ='"+ loginPwd +"'";
                System.out.println(sql);

                //5.处理查询结果集(只有当第4步执行的是select语句时,才有这第5步)
                rs = stmt.executeQuery(sql); //专门执行DQL查询语句

                if(rs.next()){
                    flag = true;
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //6.释放资源(使用完资源后一定要关闭资源,java和数据库属于进程间通信,开启后一定要关闭)
                try{
                    if(stmt != null)
                        stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

                try{
                    if(conn != null)
                        conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

                try{
                    if(rs != null)
                        rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }

            return flag;

        }

        /**
         * 初始化用户界面
         * @return 用户输入用户名、密码等信息
         */
        private static Map<String, String> inintUI () {
            Scanner s = new Scanner(System.in);

            System.out.print("用户名:");
            String loginName = s.nextLine();
            System.out.print("密码:");
            String loginPwd = s.nextLine();

            Map<String, String> userLoginInfo = new HashMap<>();

            userLoginInfo.put("loginName",loginName);
            userLoginInfo.put("loginPwd",loginPwd);

            return userLoginInfo;

        }

}

2.SQL注入的解决——PreparedStatement

2.1.改动1——Statement改为PreparedStatement
  •   //        Statement stmt = null;
      PreparedStatement stmt = null;
    
2.2.改动:2——改为占位符—?
  •   //String sql = " select * from t_user where loginName ='"+ loginName +"'and loginPwd ='"+ loginPwd +"'";
      //一个问号表示一个占位符
      String sql = " select * from t_user where loginName = ? and loginPwd = ? ";
    
2.3.改动:3——prepareStatement进行预编译
  •   // stmt = conn.createStatement();
      //程序执行到此,会发送sql语句”框子“给DBMS,然后它进行‘预编译
      stmt = conn.prepareStatement(sql);
    
2.4.改动:4—— 给占位符传值
  •   stmt.setString(1,loginName);
      stmt.setString(2,loginPwd);
    
public class JDBCTestSqlInjectSolve {
    public static void main(String[] args) {
        //1.输入信息
        Map<String, String> userLoginInfo = inintUI();

        //2.验证信息是否正确
        boolean loginSuccess = login(userLoginInfo);
        System.out.println(loginSuccess ? "登陆成功" : "登陆失败");

    }
    /**
     * 验证用户名,密码是否正确
     * @param userLoginInfo
     * @return
     */
    private static boolean login (Map < String, String > userLoginInfo){
        boolean flag=false;
        String  loginName = userLoginInfo.get("loginName");
        String  loginPwd = userLoginInfo.get("loginPwd");
        //JDBC代码
        Connection conn = null;
//改动1:
//        Statement stmt = null;
        PreparedStatement stmt = null;

        ResultSet rs = null;


        try {
            //1.注册驱动(作用:告诉java程序,即将要连接哪个品牌的数据库)
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接(表示JVM的进程和数据库进程之间的  通道 打开了,使用完之后  必须关闭)
            String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址链接
            String user = "root";
            String password = "333";
            conn = DriverManager.getConnection(url,user,password);
            //3.获取数据库操作对象(专门执行sql语句的对象)

//改动:2
//            String sql = " select * from t_user where loginName ='"+ loginName +"'and loginPwd ='"+ loginPwd +"'";
            //一个问号表示一个占位符
            String sql = " select * from t_user where loginName = ? and loginPwd = ? ";
//改动:3
//            stmt = conn.createStatement();
            //程序执行到此,会发送sql语句”框子“给DBMS,然后它进行‘预编译
            stmt = conn.prepareStatement(sql);
//改动:4      给占位符传值
            stmt.setString(1,loginName);
            stmt.setString(2,loginPwd);


            //4.执行sql语句(主要执行DQL、DML……)
            rs = stmt.executeQuery(); //专门执行DQL查询语句
            //5.处理查询结果集(只有当第4步执行的是select语句时,才有这第5步)


            if(rs.next()){
                flag = true;
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6.释放资源(使用完资源后一定要关闭资源,java和数据库属于进程间通信,开启后一定要关闭)
            try{
                if(stmt != null)
                    stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try{
                if(conn != null)
                    conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try{
                if(rs != null)
                    rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }

        return flag;

    }

    /**
     * 初始化用户界面
     * @return 用户输入用户名、密码等信息
     */
    private static Map<String, String> inintUI () {
        Scanner s = new Scanner(System.in);

        System.out.print("用户名:");
        String loginName = s.nextLine();
        System.out.print("密码:");
        String loginPwd = s.nextLine();

        Map<String, String> userLoginInfo = new HashMap<>();

        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);

        return userLoginInfo;

    }

}

3.对比Statement 与 PreparedStatement

3.1.区别:
  • statement存在SQL注入问题,PreparedStatement解决了SQL注入问题

  • Statement是编译一次执行一次,PreparedStatement是编译一次,可执行N次,效率较高

  • PreparedStatement会在编译阶段做类型的安全检查

  • 综上:99%的情况使用PreparedStatement,1%的情况使用Statement

3.2.什么情况使用Statement?
  • 业务要求必须进行SQL注入时
  • Statement支持注入,凡是要求需要进行sql语句拼接的,必须使用Statement
3.2.1. 比如::::把emp表内的数据按工资
  • desc排列
  • asc 排列

Statement实现:
在这里插入图片描述
在这里插入图片描述

public class JDBCStatementOnly {
        public static void main(String[] args) {

            //JDBC代码
            Connection conn = null;
//改动1:
            Statement stmt = null;
//            PreparedStatement stmt = null;

            ResultSet rs = null;



            //排列方式:
            Scanner s = new Scanner(System.in);
            System.out.print("desc or asc ?:");
            String order = s.nextLine();

            try{

                //1.注册驱动(作用:告诉java程序,即将要连接哪个品牌的数据库)
                Class.forName("com.mysql.jdbc.Driver");
                //2.获取连接(表示JVM的进程和数据库进程之间的  通道 打开了,使用完之后  必须关闭)
                String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址链接
                String user = "root";
                String password = "333";
                conn = DriverManager.getConnection(url,user,password);
                //3.获取数据库操作对象(专门执行sql语句的对象)
                stmt = conn.createStatement();

                //4.执行sql语句(主要执行DQL、DML……)
                String sql = " select * from emp order by sal "+order;


                //5.处理查询结果集(只有当第4步执行的是select语句时,才有这第5步)
                rs = stmt.executeQuery(sql); //专门执行DQL查询语句
                boolean flag = rs.next();

                while (flag){
                    //列表标签
                    String empno = rs.getString("empno");
                    String ename = rs.getString("ename");
                    String sal = rs.getString("sal");

                    System.out.println(empno+","+ename+","+sal);
                    flag = rs.next();
                }


            }catch (Exception e){
                e.printStackTrace();
            }finally { //为保证一定关闭,放在finally关闭
                //6.释放资源(使用完资源后一定要关闭资源,java和数据库属于进程间通信,开启后一定要关闭)
                //按照从小到大的顺序关闭
                try{
                    if(stmt != null)
                        stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

                try{
                    if(conn != null)
                        conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }

                try{
                    if(rs != null)
                        rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }


            }

        }

    }

preparedStatement 无法实现需求:
在这里插入图片描述

/***
 * 把emp表内的数据按工资
 *  *              desc排列
 *  *              asc 排列
 *      preparedStatement 无法实现需求
 */
class JDBCpreparedStatementOnly {
    public static void main(String[] args) {

        //JDBC代码
        Connection conn = null;
//改动1:

        PreparedStatement stmt = null;

        ResultSet rs = null;



        //排列方式:
        Scanner s = new Scanner(System.in);
        System.out.print("desc or asc ?:");
        String order = s.nextLine();

        try{

            //1.注册驱动(作用:告诉java程序,即将要连接哪个品牌的数据库)
            Class.forName("com.mysql.jdbc.Driver");
            //2.获取连接(表示JVM的进程和数据库进程之间的  通道 打开了,使用完之后  必须关闭)
            String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址链接
            String user = "root";
            String password = "333";
            conn = DriverManager.getConnection(url,user,password);
            //3.获取数据库操作对象(专门执行sql语句的对象)
            String sql = " select * from emp order by sal  ? ";
            stmt = conn.prepareStatement(sql);
            //改动:4      给占位符传值
            stmt.setString(1,order);


            //4.执行sql语句(主要执行DQL、DML……)


            //5.处理查询结果集(只有当第4步执行的是select语句时,才有这第5步)
            rs = stmt.executeQuery();

            boolean flag = rs.next();

            while (flag){
                //列表标签
                String empno = rs.getString("empno");
                String ename = rs.getString("ename");
                String sal = rs.getString("sal");

                System.out.println(empno+","+ename+","+sal);
                flag = rs.next();
            }


        }catch (Exception e){
            e.printStackTrace();
        }finally { //为保证一定关闭,放在finally关闭
            //6.释放资源(使用完资源后一定要关闭资源,java和数据库属于进程间通信,开启后一定要关闭)
            //按照从小到大的顺序关闭
            try{
                if(stmt != null)
                    stmt.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try{
                if(conn != null)
                    conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try{
                if(rs != null)
                    rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }


        }

    }

}

3.3.为什么有些情况Statement可以,PreparedStatement不行????
  • 以3.2为例:把emp表内的数据按工资升序、降序排列
  • 正常sql语句应该是:
  •    select empno ,ename,sal from emp order by sal desc;//降序
    
  •    select empno ,ename,sal from emp order by sal asc; //升序
    

在这里插入图片描述

  • Statement程序
  •   //4.执行sql语句(主要执行DQL、DML……)
     String sql = " select * from emp order by sal "+order;
     System.out.println(sql);
    

在这里插入图片描述

  • PreparedStatement程序
  •   //3.获取数据库操作对象(专门执行sql语句的对象)
          String sql = " select * from emp order by sal  ? ";
      //预编译
          stmt = conn.prepareStatement(sql);
      //给占位符传值
          stmt.setString(1,order);
          System.out.println(sql);
    

在这里插入图片描述

4.PreparedStatement的CRUD

4.1.增insert:向表中插入数据——(no=14,name=Bob,class_id=2020)
  •   	 //增  //insert into t_stu(no,name,class_id) values (14,'Bob',2020);
          String sql = "insert into t_stu(no,name, class_id ) values (?,?,?)";
          ps = conn.prepareStatement(sql);
          //      给占位符传值
          ps.setInt(1,14);
          ps.setString(2,"Bob");
          ps.setInt(3,2020);
          ps.executeUpdate();
    
4.2.删delete:删除no>20的全部数据
  •   		//删     delete from t_stu  where no>20;
          String sql3 = "delete from t_stu  where no>?";
          ps = conn.prepareStatement(sql3);
          //      给占位符传值
          ps.setInt(1,20);
          ps.executeUpdate();
    
4.3.改update:no=20的数据,name都改为Anna
  •   //      //改 update t_stu set name='Anna' where no = 20 ;
          String sql2 = "update t_stu set name= ? where no=?";
          ps = conn.prepareStatement(sql2);
         // 给占位符传值
          ps.setString(1,"Anna");
          ps.setInt(2,20);
          ps.executeUpdate();
    
4.4.查retrieve:查询no>10的所有数据
  •   //查	select * from t_stu where no>10;
          String sql4 = " select * from t_stu where no>?";
          ps = conn.prepareStatement(sql4);
      // 给占位符传值
          ps.setInt(1,10);
         
      //4.执行sql语句(主要执行DQL、DML……)
          rs = ps.executeQuery(); //专门执行DQL查询语句
    
/**
 * 演示preparedStatement 的 CRUD
 */


import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.*;

public class preparedStatement {
    public static void main(String[] args) {
        //JDBC代码
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
        //1.注册驱动(作用:告诉java程序,即将要连接哪个品牌的数据库)
            Class.forName("com.mysql.jdbc.Driver");
        //2.获取连接(表示JVM的进程和数据库进程之间的  通道 打开了,使用完之后  必须关闭)
            String url = "jdbc:mysql://localhost:3306/bjpowernode"; //自己的地址链接
            String user = "root";
            String password = "333";
            conn = DriverManager.getConnection(url,user,password);
        //3.获取数据库操作对象(专门执行sql语句的对象)
            //一个问号表示一个占位符


        //增     insert into t_stu(no,name, class_id ) values (?,?,?)
            String sql = "insert into t_stu(no,name, class_id ) values (?,?,?)";
            //insert into t_stu(no,name,class_id) values (20,'James',2020);
            ps = conn.prepareStatement(sql);
            //      给占位符传值
            ps.setInt(1,14);
            ps.setString(2,"Bob");
            ps.setInt(3,2020);
            ps.executeUpdate();



//        删     delete from t_stu  where no=30;
            String sql3 = "delete from t_stu  where no>?";
            ps = conn.prepareStatement(sql3);
            //      给占位符传值
            ps.setInt(1,20);
            ps.executeUpdate();


//      //改 update t_stu set name='dior' where no > ?;
            String sql2 = "update t_stu set name= ? where no=25?";
            ps = conn.prepareStatement(sql2);
//          给占位符传值
            ps.setString(1,"Anna");
            ps.setInt(2,20);
            ps.executeUpdate();



//查
            String sql4 = " select * from t_stu where no>?";
            ps = conn.prepareStatement(sql4);
//          给占位符传值
            ps.setInt(1,10);
            //程序执行到此,会发送sql语句”框子“给DBMS,然后它进行‘预编译


        //4.执行sql语句(主要执行DQL、DML……)
            rs = ps.executeQuery(); //专门执行DQL查询语句
        //5.处理查询结果集(只有当第4步执行的是select语句时,才有这第5步)
            boolean flag = false;
            flag = rs.next();
            while (flag){
                //列表标签
                String no = rs.getString("no");
                String name = rs.getString("name");
                String class_id = rs.getString("class_id");

                System.out.println(no+","+name+","+class_id);
                flag = rs.next();
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6.释放资源(使用完资源后一定要关闭资源,java和数据库属于进程间通信,开启后一定要关闭)

            try{
                if(ps != null)
                    ps.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try{
                if(conn != null)
                    conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try{
                if(rs != null)
                    rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值