JDBC学习——简单学习以及sql注入现象!

2023-04-16 14:47:27 浏览数 (2)

  JDBC (java语言连接数据库)是sun公司制定的一套接口   JDBC 代码编写的六步  1、注册驱动  2、获取连接  3、获取数据库操作对象  4、执行sql语句  5、处理查询结果集  6、释放资源


1、注册驱动   有两种方式:

代码语言:javascript复制
 //第一种
Driver driver=new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver);
//第二种注册方式(常用)
 Class.forName("com.mysql.jdbc.Driver");

  在编写JDBC的时候可以通过使用资源绑定器(xxx.properties) 绑定属性配置文件和工具类的创建去让代码看起来更简介方便。   将连接数据库的所有信息配置到文件当中xxx.properties,因为实际开发中不建议把连接数据库的信息 写死到java程序中

案例:模拟用户登录

代码语言:javascript复制
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;

/**
 *  实现功能
 *      1. 需求:模拟用户登录功能的实现
 *      2. 业务描述:
 *          程序运行的 时候提供一个输入的入口  让用户可以输入用户名和密码
 *          用户输入后提交信息 java程序收集到用户信息
 *          java程序验证用户名和密码是否合法
 *          合法:显示登录成功
 *          不合法:显示登录失败
 *      3. 数据的准备
 *          实际开发中表的设计会使用专业的建模工具
 *           ---- ---------- ----------- ---------- 
 *          | id | loginPwd | loginName | realName |
 *           ---- ---------- ----------- ---------- 
 *          |  1 | 111      | 1         | 1        |
 *          |  2 | 222      | 2         | 2        |
 *           ---- ---------- ----------- ---------- 
 *      4.当前程序存在的问题
 *          用户名:fdsa
 *          密码:fdsa' or '1'='1
 *          登录成功!
 *          这种现象叫做SQL注入(安全隐患)(黑客经常使用)
 *       5.导致SQL注入的主要原因
 *          where loginPwd = 'fdsa' or '1'='1'
 *          这样会导致sql 的代码变成上面那样  因为1=1 恒成立  所以会查询到所有字段
 *          用户输入的信息含有sql语句的关键字,
 *          而最重要的是这些关键字参与了sql语句 的编译过程
 *          导致sql语句原意扭曲 进而达到sql注入
 */
public class JDBC_Test06 {
    public static void main(String[] args) {
        //初始化一个界面
        /* 该方法要返回一个容器 既可以存用户名 又可以存密码*/
        Map<String,String> userLoginInfo = initUI();
        //验证用户名loginName和密码loginPwd
        boolean loginResult = login(userLoginInfo);
        System.out.println(loginResult ? "登录成功!":"登录失败!");
    }

    /**
     * 初始化用户界面
     * @return loginName loginPwd 等登录信息
     */
    private static Map<String,String> initUI(){
        Scanner scanner=new Scanner(System.in);
        System.out.print("用户名:");
        String loginName = scanner.nextLine();
        System.out.print("密码:");
        String loginPwd = scanner.nextLine();
        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName",loginName);
        userLoginInfo.put("loginPwd",loginPwd);
        return userLoginInfo;
    }

    /**
     *
     * @param userLoginInfo
     * @return boolean
     */
    private static boolean login(Map<String,String> userLoginInfo){
        //打标记
        boolean loginResult = false;
        /*
        P_JDBC_Test01.properties中的代码
        driver = com.mysql.jdbc.Driver
        url = jdbc:mysql://localhost:3306/bjpowernode
        user = root
        password = 020826
        */
        //JDBC代码
        Connection connection = null;
        Statement statement=null;
        ResultSet resultSet=null;
        ResourceBundle resourceBundle = ResourceBundle.getBundle("P_JDBC_Test01");
        String driver = resourceBundle.getString("driver");
        String url = resourceBundle.getString("url");
        String user = resourceBundle.getString("user");
        String password = resourceBundle.getString("password");
        try {
            //1. 注册驱动
            Class.forName(driver);
            //2. 获取连接
            connection = DriverManager.getConnection(url,user,password);
            //3. 获取数据操作对象
            statement = connection.createStatement();
            //4. 执行sql
           /* String sql = "select * from t_user where loginName = 'xxx' and loginPwd = ''";*/
            /*
            变量拼到一个字符串   重点
            loginName = '" userLoginInfo.get("loginName") "'
            */
            String sql = "select * from t_user where loginName = '" userLoginInfo.get("loginName") "' and loginPwd = '" userLoginInfo.get("loginPwd") "'";
            //上面这行进行了sql语句的拼接  下面这行发送sql语句到DBMS DBMS 进行sql 编译
            //正好将用户提供的非法信息编译进去 导致了原sql语句含义改变
            resultSet = statement.executeQuery(sql);
            //5. 处理查询结果集
            /* 这个例子中 用户不匹配查不到记录  用户匹配了也只能查到1条记录 所以不需要用while 用if就可与*/
            if (resultSet.next()){
                //如果为true 说明结果集中又数据 这个例子中 有数据代表用户匹配
                loginResult = true;
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //6. 释放资源
            try {
                if (resultSet!=null){
                    resultSet.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            try {
                if (statement != null) {
                    statement.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            try {
                if (connection != null) {
                    connection.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }


        return loginResult;
    }
}

上述案例存在sql注入现象 解决方法 使用java.sql.preparedStatement(预编译的数据库操作对象) 原理: preparedStatement预先对sql语句进行了编译 然后再给SQL语句传"值" 用preparedStatement后第三步获取数据库操作对象和第四步执行sql会发生变化

代码语言:javascript复制
//3. 获取预编译的数据操作对象
                //  ? 占位符 1个?将来会接收1个值
                //  SQL语句的框架
            String sql = "select * from t_user where loginName = ? and loginPwd = ? ";
                //程序执行到此会发送sql语句到DBMS 然后DBMS会进行sql语句的预编译
            /*prepareStatement 这个没有 d */
            preparedStatement = connection.prepareStatement(sql);
                //给 占位符 ? 传值 第一个 ? 下标是1 第二个是2  JDBC中下标从1开始
            preparedStatement.setString(1,userLoginInfo.get("loginName"));
            preparedStatement.setString(2,userLoginInfo.get("loginPwd"));

            //4. 执行sql
            resultSet = preparedStatement.executeQuery();

对比 Statement 和 preparedStatement Statement 存在SQL注入问题 Statement 编译一次 执行一次 preparedStatement 编译一次 执行n次 preparedStatement大多情况使用 ,只有极少数情况使用Statement,当业务必须需要sql语句拼接 或sql注入的时候。

0 人点赞