哪有什么一夜成名,都是百炼成钢

JDBC

JDBC(Java DataBase Connectivity,java数据库连接)又SUN公司开发,是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
简单地说,JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。
代码示例:

Connection con = DriverManager.getConnection("jdbc:odbc:wombat","login",
"password");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}

对以上代码分析

Connection 连接 代表了java和数据之间的通道,桥梁
Statement  语句 可以用来执行 insert, update, delete , select ...
ResultSet  结果集 代表的是查询的结果

类
DriverManager 工具类,获取连接对象
SQLException  异常对象 是 Exception的子类型,属于检查异常

操作步骤顺序

  1. 加载驱动 (Driver) jdbc的驱动就是一个连接工厂,生成的产品是连接对象
    com.mysql.jdbc.Driver 是Driver的mysql实现类
    oracle.jdbc.Driver 是Driver的oracle实现类
   Class.forName("驱动类名");
   例如:
   Class.forName("com.mysql.jdbc.Driver");

jdbc 3.0 以上版本都可以省略加载驱动这一步

  1. 获取连接对象
   DriverManager.getConnection(url, 用户名, 密码); // 内部调用了 Driver 对象获取数据库连接

url 的格式 :

jdbc:mysql://ip地址:端口号/数据库名?参数

例如:mysql数据库

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
  1. 创建语句
Statement stmt = conn.createStatement();
  1. 执行sql
int rows = stmt.executeUpdate(); // 用来执行 insert , update, delete , DDL , 返回值代表影响行数
// delete from student where sid = 1008; 返回影响行数是0;

ResultSet rs = stmt.executeQuery(); // 用来执行 select
boolean moreRows = rs.next() 取得下一条记录, 
moreRows 是true ,表示有下一条记录, false 表示没有了
while(rs.next()) {

}
// 迭代器模式
  1. 释放资源 ( 先打开的资源后关闭 )
rs.close();
stmt.close();
conn.close();

JDBC及sql注入攻击

1.创建一个user表,用来登录比对。

create table user(username varchar(50) not null,password varchar(50));

2.添加user信息。

insert into user(username,password) values ('张三','123');

3.通过jdbc连接sql数据库具体代码示例。
首先测试一下jdbc的连接,通过sql语句

select * from user where username='张三' and password='123';

来查询出张三的账号密码
代码如下(通过字符串拼接实现select传参)

public class TestJdbc {
    public static void main(String[] args) throws SQLException {
   
        getSelect("张三","123");//用户名 张三 密码 123
        return;
    }
    private static void getSelect(String name, String psd) throws SQLException {
        //1.创建连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        //2.准备sql语句
        Statement statement = conn.createStatement();
        //3.执行sql语句并返回一个集合
        ResultSet resultSet = statement.executeQuery("select * from user where username='"+name+"' and  password='"+psd+"'");
        //4.如果有下一条数据返回true,没有返回false
        while (resultSet.next()){
            //resultSet本身值在在第一条记录的前面.
            System.out.println(resultSet.getString("username")+resultSet.getString("password"));
        }
        statement.close();
        conn.close();
    }
}

输出结果
张三123

上面我们传的参数是‘张三’,‘123’ 都是正确的账号密码
那么,接下来我们将参数换一下密码看能否到正确的信息
要替换的参数如下

getSelect("张三","'or '1'='1");

将密码换成“'or ‘1’='1”。

public class TestJdbc {
    public static void main(String[] args) throws SQLException {

        getSelect("张三","'or '1'='1");//用户,密码
        return;
    }
    private static void getSelect(String name, String psd) throws SQLException {
        //1.创建连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        //2.准备sql语句
        Statement statement = conn.createStatement();
        //3.执行sql语句并返回一个集合
        ResultSet resultSet = statement.executeQuery("select * from user where username='"+name+"' and  password='"+psd+"'");
        //4.如果有下一条数据返回true,没有返回false
        while (resultSet.next()){
            //resultSet本身值在在第一条记录的前面.
            System.out.println(resultSet.getString("username")+resultSet.getString("password"));
        }
        statement.close();
        conn.close();
    }
}

输出结果
张三123

咦,到这里是不是有点奇怪,明明密码是123 随便写了个“'or ‘1’='1”也行,其实我们将我们的sql语句拿出来分析一下,答案显而易见。

select * from user where username='"+name+"' and  password='"+psd+"

这是我们的sql语句,我们将第一次的参数 “张三” “'or ‘1’='1”带入得到新的sql.

select * from user where username='张三' and  password=''or '1'='1'

看到这里是不是发现了问题的真正所在,我们输入的值直接将本来的sql语句都给发生了改变。
那么,如何防治这种情况出现呢??
1.对参数内存做检查,内部不能有sql关键字例如:or
2.用 PreparedStatement代替Statement

PreparedStatement 预编译语句对象

Statement stmt = conn.createStatement();
stmt.executeUpdate(String sql);

  1. 需要预先提供sql语句
PreparedStatement psmt = conn.prepareStatement(String sql);
  1. 可以在sql中用?占位某个值
insert into username(username,password) values(?, ?)
  1. 给?赋值
使用PreparedStatement中一系列以 set开头的方法
setString(?的位置,)
setInt(?的位置,)
setDate(?的位置,)

psmt.setString(1, "张三");
psmt.setString(2, "123");
  1. 执行sql
psmt.executeUpdate();

注意: ?能够占位的只有值, 不能是表名、列名、关键字

5.用PreparedStatement的代码示例

public class TestJdbc {
    public static void main(String[] args) throws SQLException {

        getSelect("张三","123");//用户,密码
        return;
    }
    private static void getSelect(String name, String psd) throws SQLException {
        //1.创建连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
        //2.准备sql语句,用? 站位
        String sql ="select * from user where username=? and password=?";
        PreparedStatement statement = conn.prepareStatement(sql);
        设置?的值,用顺序去定位
        statement.setString(1,name);
        statement.setString(2,psd);
        //3.执行sql语句并返回一个集合
        ResultSet resultSet = statement.executeQuery();
        //4.如果有下一条数据返回true,没有返回false
        while (resultSet.next()){
            //resultSet本身值在在第一条记录的前面.
            System.out.println(resultSet.getString("username")
            +resultSet.getString("password"));
        }
        statement.close();
        conn.close();
    }
}