MySQL Day10 JDBC (内含SQL注入问题)

1 数据库驱动

驱动:声卡、显卡、数据库

我们的程序会通过数据库驱动来和数据库打交道!

在这里插入图片描述

2 JDBC

SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库)的规范,俗称JDBC

这些规范的实现由具体的厂商去做~

对于开发人员来说,我们只需要掌握JDBC接口的操作即可!

在这里插入图片描述

Idea中内置了java.sql 、javax.sql包可供我们使用!

除此之外,还需要下载一个数据库驱动包:下载地址链接

3 第一个JDBC程序

1、首先准备要连接的数据库(代码如下)

CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);

INSERT INTO `users`(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','[email protected]','1980-12-04'),
(2,'lisi','123456','[email protected]','1981-12-04'),
(3,'wangwu','123456','[email protected]','1979-12-04')

2、导入数据库驱动

在新建的工程下创建lib目录,并将下载的数据库驱动.jar复制到lib目录下,如图:

在这里插入图片描述

右键单击lib目录,选择ADD as Library...导入完成后就和上图一样了!

在这里插入图片描述

随后在src目录下创建自己的类,可以参考我的创建,其中demut是我的账户名,可以自行修改:

在这里插入图片描述

3、编写测试代码

package com.demut.demo;

// 我的第一个JDBC程序

import java.sql.*;

public class jdbcFirstDemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver"); // 固定写法,加载驱动

        // 2.用户信息和url
        //?useUnicode=true&characterEncoding=utf8&useSSL=true
        //支持中文编码&设置中文集&设置安全连接
        String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true";

        String username = "root";
        String password = "123456";

        // 3.连接数据库, 使用DriverManager.getConnection()连接。
        Connection connection = DriverManager.getConnection(url,username,password);

        // 4.创建执行SQL的对象 Statement为执行sql的对象类
        Statement statement = connection.createStatement();

        // 5.执行SQL对象去执行SQL
        String sql = "SELECT * FROM users";

        ResultSet resultSet = statement.executeQuery(sql);//返回的结果集,结果集中封装了查询出来的所有结果

        while (resultSet.next()){
            System.out.println("id = " + resultSet.getObject("id"));
            System.out.println("name = " + resultSet.getObject("name"));
            System.out.println("pwd = " + resultSet.getObject("password"));
            System.out.println("email = " + resultSet.getObject("email"));
            System.out.println("birth = " + resultSet.getObject("birthday"));
            System.out.println("==========================");
        }
        // 6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}
/*运行结果
id = 1
name = zhansan
password = 123456
email = [email protected]
birthday = 1980-12-04
--------------------------
id = 2
name = lisi
password = 123456
email = [email protected]
birthday = 1981-12-04
--------------------------
id = 3
name = wangwu
password = 123456
email = [email protected]
birthday = 1979-12-04
--------------------------
*/

步骤总结:

1、加载驱动

2、连接数据库DriverManager

3、获取执行SQL的对象 Statement

4、获得返回的结果集 resultSet

5、释放连接

以上代码中用到的对象详解:

DriverManager

//DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");//加载驱动

Connection connection = DriverManager.getConnection(url, username, password);
//connection代表数据库
//数据库设置自动提交
//事务提交
//事务回滚
connection.setAutoCommit();
connection.rollback();
connection.commit();

URL

String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true";

//mysql -- 3306
//jdbc:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3

//oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid

Statement 执行SQL的对象

String sql = "SELECT * FROM users"; //编写SQL

statement.executeQuery(); //查询操作,返回ResultSet
statement.execute(); // 执行任何SQL
statement.executeUpdate(); // 更新、插入、删除都是用这个,返回一个受影响的行数

ResultSet 查询的结果集:封装了所有的查询结果

获得指定的数据类型

resultSet.getObject(); //在不知道列类型时使用
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();

遍历指针:

resultSet.beforeFirst(); //指针移动到最前面
resultSet.afterLast(); //移动到最后面
resultSet.next(); //移动到下一个数据
resultSet.previous(); //移动到前一行
resultSet.absolute(row); //移动到指定行

释放资源!

resultSet.close();
statement.close();
connection.close();

4 JDBC常用语句的封装与测试!

4.1 封装

测试了以上程序后,相信心中一定会有So tm What?的感觉~好在上述代码其实是可以封装的,以下为封装文件说明:

  • db.properties文件:内含driver、url、username、password信息。

    注意:将该文件直接创建在src目录下!

  • JdbcUtils类:内含加载数据库驱动获取了解释放连接方法

目录展示:

在这里插入图片描述

源码:

db.properties文件:

driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456

JdbcUtils类:

package com.demut.demo02.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    //加载数据库驱动
    static {
        try {
            //获取文件流
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in); //加载流
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
            //驱动只需要加载一次
            Class.forName(driver);
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    //获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    //释放连接资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if (rs!=null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st !=null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn !=null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

}

4.2 测试

以下为数据库增删改查的测试:(增删改均类似,查请特殊关注!)

TestInsert类:

package com.demut.demo02;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection(); //获取数据库连接
            st = conn.createStatement(); //获得SQL的执行对象
            String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`)\n" +
                    "VALUES (5, 'demut','123456','[email protected]','20200101')";
            int i = st.executeUpdate(sql); //返回受影响的行数
            if (i>0) {
                System.out.println("插入成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

TestDelete类:

package com.demut.demo02;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestDelete {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection(); //获取数据库连接
            st = conn.createStatement(); //获得SQL的执行对象
            String sql = "DELETE FROM users WHERE id = 4";
            int i = st.executeUpdate(sql); //返回受影响的行数
            if (i>0) {
                System.out.println("删除成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

TestUpdate类:

package com.demut.demo02;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestUpdate {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection(); //获取数据库连接
            st = conn.createStatement(); //获得SQL的执行对象
            String sql = "UPDATE users SET `name` = 'DEMUT',`email` = '[email protected]' WHERE id = 5";
            int i = st.executeUpdate(sql); //返回受影响的行数
            if (i>0) {
                System.out.println("更新成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

TestSelect类:

package com.demut.demo02;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            //SQL
            String sql = "SELECT * FROM users";
            rs = st.executeQuery(sql);//查询完毕返回一个结果集
            while (rs.next()){
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}

5 SQL 注入问题

sql存在漏洞,会被攻击导致数据泄露! SQL会被拼接(由于OR的存在)

此处使用案例说明:

编写一个login方法如下:

    //登陆业务
    public static void login(String username, String password){
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();
            String sql = "SELECT * FROM users WHERE `name` = '"+username+"' AND `password` = '"+password+"'";
            rs = st.executeQuery(sql);
            while (rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, st, rs);
        }
    }

首先使用数据库内已知的一条数据测试:

    public static void main(String[] args) {
        login("DEMUT","123456");        
    }

/*结果:
DEMUT
123456
*/

测试成功!语句被成功输出!

现在我们对SQL进行测试,盗取数据库中用户数据~

    public static void main(String[] args) {
        //login("DEMUT","123456");
        login("' or '1=1","' or '1=1"); //此处半个单引号是为了与sql语句拼接!
    }

/*结果:
zhansan
123456
lisi
123456
wangwu
123456
DEMUT
123456
*/

SQL注入说明:由于我们输入的是账户名与密码名均为‘ ’ or '1==1'语句,所以在执行SQL语句时,Statement默认输入的值全为true,则将数据库中所有的用户信息查询了出来,造成信息泄露。SQL注入已经危害了很多网站,几乎所有数据库都会存在注入问题,为了规避这种现象,我们今后使用PreparedStatement对象操作!

6 PreparedStatement对象

PreparedStatement可以防止SQL注入,效率更高!

与Statement不同的是,PreparedStatement采用了预编译,且使用?占位符代替参数,方便输入且更加安全!

使用步骤:

  1. 编写SQL语句
  2. 预编译
  3. 设置参数
  4. 执行

插入语句测试:

package com.demut.demo03;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.*;
import java.util.Date;

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //区别
            //使用?占位符代替参数
            String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)";
            pst = conn.prepareStatement(sql); //预编译SQL,先写sql,不执行

            //手动给参数赋值:
            pst.setInt(1,4); //此处的1表示第一个问好
            pst.setString(2,"Jever");
            pst.setString(3,"111111");
            pst.setString(4,"[email protected]");
            //注意: sql.Date 数据库     java.sql.Date()
            //      util.Date  Java     new Date().getTime() 获得时间戳
            pst.setDate(5,new java.sql.Date(new Date().getTime()));

            int i = pst.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,pst,rs);
        }
    }
}
/*结果:
插入成功!
*/

删除语句测试:

package com.demut.demo03;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class TestDelete {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //区别
            //使用?占位符代替参数
            String sql = "DELETE FROM users WHERE id=?";
            pst = conn.prepareStatement(sql); //预编译SQL,先写sql,不执行

            //手动给参数赋值:
            pst.setInt(1,5); //此处的1表示第一个问好

            int i = pst.executeUpdate();
            if (i > 0) {
                System.out.println("删除成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,pst,rs);
        }
    }
}
/*结果:
删除成功!
*/

更新语句测试:

package com.demut.demo03;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class TestUpdate {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //区别
            //使用?占位符代替参数
            String sql = "update users set `name` = ? where id=?;";
            pst = conn.prepareStatement(sql); //预编译SQL,先写sql,不执行

            //手动给参数赋值:
            pst.setString(1,"LiHua");
            pst.setInt(2,1);

            int i = pst.executeUpdate();
            if (i > 0) {
                System.out.println("更新成功!");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,pst,rs);
        }
    }
}
/*结果:
更新成功!
*/

查询语句测试:

package com.demut.demo03;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "SELECT * FROM users WHERE id = ?"; //编写SQL
            pst = conn.prepareStatement(sql); //预编译
            pst.setInt(1,1); //传递参数
            rs = pst.executeQuery(); //执行
            if (rs.next()) {
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, pst, rs);
        }
    }
}
/*结果
LiHua
*/

我们再来测试一下SQL注入问题:

本文第5部分中分别使用已有的数据与注入数据进行了测试,此处同上述情况,由于使用的是PreparedStatement对象,语句略有变化;同时在main函数内,两种测试情况笔者均已注释,据情况解注释即可!

源码:

package com.demut.demo03;

import com.demut.demo02.utils.JdbcUtils;

import java.sql.*;

public class SQL注入 {
    public static void main(String[] args) {
        //login("LiHua","123456");
        //login("' or '1=1","' or '1=1"); //此处半个单引号是为了与sql语句拼接!
    }

    //登陆业务
    public static void login(String username, String password){
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            String sql = "SELECT * FROM users WHERE `name`=? AND `password`=?";

            st = conn.prepareStatement(sql);
            st.setString(1,username);
            st.setString(2,password);
            rs = st.executeQuery(); //查询完毕,返回结果集
            while (rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}
/*测试结果:
情形一:(使用LiHua,123456)
LiHua
123456

情形二:(使用注入语句)
(无输出!)
*/

说明:PreparedStatement防止SQL注入的本质是:其把传递进来的参数当做字符,如果其中存在转义字符,比如 ' 会被直接转义

写在最后

Be wise in the way you act toward outsiders; make the most of every opportunity. Let your conversation be always full of grace, seasoned with salt, so that you may know how to answer everyone. (Colossians 4)

To Demut and Dottie!

发布了45 篇原创文章 · 获赞 61 · 访问量 5983

猜你喜欢

转载自blog.csdn.net/qq_44958172/article/details/105603875