Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式

一、概述

  • java.sql 包中的 PreparedStatement接口继承了Statement,也就是说: PreparedStatement接口是 Statement 接口的子接口
  • 在JDBC应用中,建议始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement
  • JDBC连接数据库的详细解释,请 点击

二、创建表 userlogin

1、代码

package class01;

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

public class Demo01 {
    public static void main(String[] args){
        Connection conn = null;
        Statement sta = null;
        String url = "jdbc:mysql://localhost:3306/db_test";
        String user = "root";
        String pwd = "lemon";

        try {
            conn = DriverManager.getConnection(url,user,pwd);
            sta = conn.createStatement();
            sta.executeUpdate("create table userlogin (id int primary key auto_increment,name varchar(20),password varchar(20))");
            System.out.println("创建成功");
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if (sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2、插入数据

insert into userlogin values (null,'lemon','lemon'),(null,'lily','lily')

Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第1张图片

三、使用 Statement 模拟登录

package class01;

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

public class Demo01{
    public static void main(String[] args){
        //在控制台输入用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();
        System.out.println("请输入密码:");
        String pwd = sc.nextLine();
        login(name,pwd);
    }
    //登录方法
    public static void login(String name,String pwd){
        Connection conn = null;
        Statement sta = null;
        ResultSet rs = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test","root","lemon");
            sta = conn.createStatement();
            //查询数据库,如果有记录则表示登录成功,否则登录失败
            String sql = "select * from userlogin where name='" + name + "' and password='" + pwd + "'";
            System.out.println(sql);
            rs = sta.executeQuery(sql);
            if (rs.next()){
                System.out.println("登录成功,欢迎" + name);
            }else{
                System.out.println("登录失败,请重新登录!!!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if (rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这段代码中,当输入 a' or '1' = '1 也会显示登录成功!
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第2张图片
问题分析:

  • select * from userlogin where name='newbady' and password='a' or '1' = '1'
    name='newbady' and password='a' 为假, '1' = '1' 为真
    相当于:select * from userlogin where true

我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,改变了原有 SQL 真正的意义,以上问题称为 SQL 注入。要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。

四、PreparedStatement 接口

1、概述

  • PreparedStatement 对象是一个预编译的SQL语句
  • PreparedStatement 在JDK中的 java.sql 包下
  • PreparedStatementStatement 接口的子接口,继承于父接口中所有的方法

2、执行原理

3、Connection 创建 PreparedStatement 对象
在这里插入图片描述
4、PreparedStatement 接口中的方法
在这里插入图片描述
5、使用 PreparedStatement 的步骤

  1. 编写 SQL 语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?";
  2. 获得 PreparedStatement 对象
  3. 设置实际参数:setXxx(占位符的位置,真实的值)
  4. 执行参数化SQL 语句
  5. 关闭资源

6、PrepareStatement 中设置参数的方法Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第3张图片
7、使用 PreparedStatement 接口修改 (三、使用 Statement 模拟登录)案例中的问题,防止 SQL 注入

package class01;

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

public class Demo01{
    public static void main(String[] args){
        //在控制台输入用户名和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String name = sc.nextLine();
        System.out.println("请输入密码:");
        String pwd = sc.nextLine();
        login(name,pwd);
    }
    //登录方法
    public static void login(String name,String pwd){
        Connection conn = null;
        Statement sta = null;
        ResultSet rs = null;
        try {
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db_test","root","lemon");
            sta = conn.createStatement();
            //查询数据库,如果有记录则表示登录成功,否则登录失败
            //String sql = "select * from userlogin where name='" + name + "' and password='" + pwd + "'";
            String sql = "select * from userlogin where name = ? and password = ?";
            //System.out.println(sql);
            PreparedStatement ps = conn.prepareStatement(sql);//得到语句对象
            //设置参数
            ps.setString(1,name);
            ps.setString(2,pwd);
            rs = ps.executeQuery();
            //rs = sta.executeQuery(sql);
            if (rs.next()){
                System.out.println("登录成功,欢迎" + name);
            }else{
                System.out.println("登录失败,请重新登录!!!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            if (rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (sta != null){
                try {
                    sta.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

8、表与类的关系
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第4张图片

五、案例——Dao模式

以 db_test 数据库中的 student 表为例
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第5张图片
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第6张图片
1、写数据库操作工具类 JdbcUtils.java

package student.jdbcutils;

import java.sql.*;

public class JdbcUtils {
    //把用户名、密码、URL、驱动类 这几个字符串定义为常量
    private static final String USER = "root";
    private static final String PWD = "lemon";
    private static final String URL = "jdbc:mysql://localhost:3306/db_test";
    private static final String DRIVER = "com.mysql.jdbc.Driver";

    //注册驱动
    static {//静态代码块,只执行一次
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //得到数据库的连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER, PWD);
    }

    //关闭所有打开的资源
    public static void close(Connection conn, Statement stm) {
        if (stm != null) {
            try {
                stm.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    //关闭所有打开的资源
    public static void close(Connection conn, Statement stm, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stm != null) {
            try {
                stm.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

2、Dao 模式具体类 StudentDao.java 接口

package student.dao;

import student.domain.Student;

import java.util.List;

public interface StudentDao {
    //写增删改查
    public int add(Student s);
    public int delete(Integer id);
    public int update(Student s);
    public List<Student> find();//查所有
    public Student findById(Integer id);//查单个
}

3、写 StudentDao 接口的实现类 StudentDaoImpl.java,用来实现接口

package student.dao;

import student.domain.Student;
import student.jdbcutils.JdbcUtils;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class StudentDaoImpl implements StudentDao{

    @Override
    public int add(Student s) {
        int i = 0;
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            //conn = DriverManager.getConnection(url,user,password);
            conn = JdbcUtils.getConnection();//引用创建好的工具类,在 jdbcutils 包下
            ps = conn.prepareStatement("insert into student values (null,?,?,?,?)");
            ps.setString(1,s.getName());
            ps.setString(2,s.getSex());
            //ps.setDate(3,s.getBirthday());//这样写不对
            ps.setDate(3,new Date(s.getBirthday().getTime()));//得到一个时间戳,通过日期函数转换成日期
            ps.setInt(4,s.getNum());
            i = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源,引用创建好的工具类,在 jdbcutils 包下
            JdbcUtils.close(conn,ps);
        }
        return i;
    }

    @Override
    public int delete(Integer id) {
        int i = 0;
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtils.getConnection();//引用创建好的工具类,在 jdbcutils 包下
            ps = conn.prepareStatement("delete from student where id = ?");
            ps.setInt(1,id);
            i = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            JdbcUtils.close(conn,ps);
        }
        return i;
    }

    @Override
    public int update(Student s) {
        int i = 0;
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = JdbcUtils.getConnection();
            ps = conn.prepareStatement("update student set name = ?,sex = ?,birthday = ?,num = ? where id = ?");
            ps.setString(1,s.getName());
            ps.setString(2,s.getSex());
            ps.setDate(3,new Date(s.getBirthday().getTime()));
            ps.setInt(4,s.getNum());
            ps.setInt(5,s.getId());
            i = ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.close(conn,ps);
        }
        return i;
    }

    @Override
    public List<Student> find() {
        List<Student> list = new ArrayList<>();
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            ps = conn.prepareStatement("SELECT * FROM student");
            rs = ps.executeQuery();

            while (rs.next()) {
                Student s = new Student();
                s.setId(rs.getInt("id"));
                s.setName(rs.getString("name"));
                s.setSex(rs.getString("sex"));
                s.setBirthday(rs.getDate("birthday"));
                s.setNum(rs.getInt("num"));
                list.add(s);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(conn, ps, rs);
        }
        return list;
    }

    @Override
    public Student findById(Integer id) {
        Student s = null;
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            ps = conn.prepareStatement("SELECT * FROM student where id = ?");

            ps.setInt(1, id);
            rs = ps.executeQuery();

            while (rs.next()) {
                s = new Student();
                s.setId(rs.getInt("id"));
                s.setName(rs.getString("name"));
                s.setSex(rs.getString("sex"));
                s.setBirthday(rs.getDate("birthday"));
                s.setNum(rs.getInt("num"));
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils.close(conn, ps, rs);
        }
        return s;
    }
}

4、封装 Student.java 实体类,与数据表对应

package student.domain;

import java.util.Date;

public class Student {
    private Integer id;
    private String name;
    private String sex;
    private Date birthday;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Integer getNum() {
        return num;
    }

    public void setNum(Integer num) {
        this.num = num;
    }

    private Integer num;
}

5、测试类 StudentDaoTest.java ,输出数据表的数据

package student.test;

import student.dao.StudentDao;
import student.dao.StudentDaoImpl;
import student.domain.Student;

import java.util.List;

public class StudentDaoTest {
    public static void main(String[] args){
        StudentDao dao = new StudentDaoImpl();
        //查询
        List<Student> list = dao.find();
        for (Student student : list) { //增强 for 循环,快捷 iter
            System.out.println(student.getId() + "\t" +
                    student.getName() + "\t" +
                    student.getSex() + "\t" +
                    student.getBirthday() + "\t" +
                    student.getNum());
        }
    }
}

Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第7张图片
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第8张图片
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第9张图片
Java SQL接口 PreparedStatement 防止SQL注入 以及Dao模式_第10张图片

package student.test;

import student.dao.StudentDao;
import student.dao.StudentDaoImpl;

public class StudentDaoTest {
    public static void main(String[] args) {
        StudentDao dao = new StudentDaoImpl();
        //在表中删除一条数据
        dao.delete(5);
    }
}

以上代码太过复杂,查看以Spring中的JdbcTemplete类简化后的 Dao 模式请点击

你可能感兴趣的:(数据库,Java)