5. MySQL - JDBC & SQL 注入 &博客系统(万字详解)

这篇具有很好参考价值的文章主要介绍了5. MySQL - JDBC & SQL 注入 &博客系统(万字详解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 介绍

2. 使用 JDBC 连接数据库

2.1 如何使用 JDBC 连接数据库

2.2 导入的各个类

2.3 DataSource 对象的创建

2.4 从 DataSource 对象中得到 Connection 对象

2.5 创建 Statement 对象

2.6 从 ResultSet 中遍历每行结果,从每行中获取每列的值

2.7 代码汇总

3. PrepareStatement 详解

3.1 动态 SQL 执行

3.2 SQL 类型

3.3 SQL 注入(Inject)

4. 关于 SQL 的执行

4.1 带查询结果的执行

4.2 不带查询结果的执行

4.3 关于 ResultSet 读取列

5. 实践应用:博客系统

发表文章

删除文章ByBid

批量删除文章ByBid


1. 介绍

什么是 JDBC 呢?

JDBC 代表 Java 数据库连接(JavaDatabaseConnectivity),它是用于 Java 编程语言和数据库之间的数据库无关连接的标准 Java API。

我们写的所有程序的代码来自:官方提供(JDK)、我们写的(App)。

为了减少工作量,引入一些别人(不是官方和我们)写的代码,一般把这类代码称为第三方。以库(library lib)。

在 Java 中,一般是以一组类文件(*.class)提供。把这组类文件打出一个文件(采用 zip 压缩格式,Java 称为 Jar 包:Java ARchive)。

所以,我们要使用别人的第三方库,需要:

  1. 拿到一个 jar 包

  2. 配置工程(IDEA)让工程在编写、编译、运行阶段,都可以找到第三方库中的类

我们要使用一个 MySQL 官方提供的,进行 SQL 查询的代码库 "mysql-connector-java-5.1.47.jar"

描述:我们的应用 依赖于(depends on)"mysql-connector-java-5.1.47.jar"

为了处理好这些依赖关系,引入 Java 构建阶段的工具:maven/gradle。

这类工具,为了定位每个库,定义了一个坐标(coordinate)的概念。

这个库属于哪个组织(公司):groupId

这个库自己的专有名字:artifactId

这个库的当前版本:version

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&MySQL,数据库,mysql,sql

groupId + artifactId + version 唯一确定一个库。

pom.xml 关于 Maven 工具的配置文件,我们的依赖关系要在这里写清楚。

POM 的主体结构:

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&MySQL,数据库,mysql,sql

在新建一个 maven 项目后,我们需要添加依赖,使用以下代码即可。

<properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <encoding>utf-8</encoding>
</properties>

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
</dependencies>

如下图所示:

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&amp;MySQL,数据库,mysql,sql

2. 使用 JDBC 连接数据库

2.1 如何使用 JDBC 连接数据库
  • DataSource(数据源)

  • Connection(连接)

  • Statement(SQL 语句)/ PreparedStatement

  • ResultSet(结果集)

对比打电话的例子:

DataSource 类比 联系方式 or 通讯录

Connection 类比打通电话,需要 DataSource 作为前提

Statement:客户向服务器发送的 SQL 语句

ResultSet:服务器向客户回的结果

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&amp;MySQL,数据库,mysql,sql

2.2 导入的各个类
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
2.3 DataSource 对象的创建

DataSource 只是 JDBC 提供的一个接口,要创建的是 MysqlDataSource 对象。

数据库的相关信息         

1. 数据库所在的主机:127.0.0.1 / localhost 

2. 数据库所在的端口:3306 / 3307 ... 

3. 数据库的用户名: ... 

4. 数据库的密码: ...

5. 额外的配置选项

        5.1. 连接使用的字符集编码 characterEncoding=utf8  

        5.2. 不使用加密连接: useSSL=false  

        5.3. 指定时区: serverTimezone=Asia/Shanghai

第一种创建方式:

MysqlDataSource dataSource = new MysqlDataSource();

dataSource.setServerName("127.0.0.1");
dataSource.setPort(3306);
dataSource.setUser("debian-sys-maint");
dataSource.setPassword("*********");
dataSource.setDatabaseName("learn");
dataSource.setCharacterEncoding("utf8");
dataSource.setUseSSL(false);
dataSource.setServerTimezone("Asia/Shanghai");

第二种创建方式:

MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser("debian-sys-maint");
dataSource.setPassword("*********");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/learn?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");
2.4 从 DataSource 对象中得到 Connection 对象
// try-with-resource
// 利用这种写法,不用自己写 con.close(),代码结构看起来干净
try (Connection con = dataSource.getConnection()) {
    // 利用 con 对象执行 SQL
} catch (SQLException exc) {
    // 处理异常
}
2.5 创建 Statement 对象
// -- 列出当前所在库的所有表名称
String sql = "show tables";

try (PreparedStatement ps = con.prepareStatement(sql)) {
    // execute: 执行
    // query: 查询
    try (ResultSet rs = ps.executeQuery()) {
        // 结果就可以从 ResultSet 对象中获取
    }
}
2.6 从 ResultSet 中遍历每行结果,从每行中获取每列的值
// hasNext() + next()
while (rs.next()) {
    // rs 代表当前遍历的行
    // 第一列的结果是表的名称,所以是字符串 String
    String tableName = rs.getString(1);    // 下标是 1
    System.out.println(tableName);
}
2.7 代码汇总
package com.peixinchen;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class Main {
    public static void main(String[] args) throws SQLException {
        MysqlDataSource dataSource = new MysqlDataSource();

        dataSource.setUser("debian-sys-maint");
        dataSource.setPassword("***************");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/learn?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");

        // try-with-resource
        // 利用这种写法,不用自己写 con.close(),代码结构看起来干净
        try (Connection con = dataSource.getConnection()) {
            // 利用 con 对象执行 SQL

            // -- 列出当前所在库的所有表名称
            String sql = "show tables"; // 只有 1 列

            try (PreparedStatement ps = con.prepareStatement(sql)) {
                try (ResultSet rs = ps.executeQuery()) {
                    // 结果就可以从 ResultSet 对象中获取

                    // hasNext() + next()
                    while (rs.next()) {
                        // rs 代表当前遍历的行
                        // 第一列的结果是表的名称,所以是字符串 String
                        String tableName = rs.getString(1);    // 下标是 1
                        System.out.println(tableName);
                    }
                }
            }
        }
    }
}

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&amp;MySQL,数据库,mysql,sql

进程的执行结果 = 程序代码 + 进程执行时的环境

同样的代码,不同的环境是可能得到不同的结果的,所以如果遇到了运行结果不符合预期,除了检查代码的问题之外,现在应该把更多精力放到检查周边环境是否符合预期上了。

3. PrepareStatement 详解

3.1 动态 SQL 执行
// 静态 SQL
String sql = "show tables";
String sql = "select * from oj_records where oj_id = 1"

我们在之前使用的都是静态的 SQL ,但是在实际的应用中,大多都是动态的 SQL ,比如用户登陆界面都是等待用户输入数据,并不是固定的数据。因此,我们来了解一下动态 SQL。

PreparedStatement 通过占位符(placeholder)和动态绑定的方式做到。

// ? 作为占位符,先把位置占住,等待用户输入
String sql = "select * from users where username = ? and password = ?";

PreparedStatement ps = conn.preparedStatement(sql);

String username = scanner.nextLine();
String password = scanner.nextLine();

// 用实际得到的用户名和密码替换占位符 —— 动态绑定
ps.setString(1, username);    // 用用户名替换第一个 ?
ps.setString(2, password);    // 用密码替换第二个 ?

接下来,我们运行以下完整的代码(密码和库更改为自己的即可):

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

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

public class PreparedStatementDemo {
    public static void main(String[] args) throws SQLException{
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要查询的难度: ");
        String difficulty = scanner.nextLine();

        MysqlDataSource dataSource = new MysqlDataSource();

        dataSource.setUser("debian-sys-maint");
        dataSource.setPassword("*****************");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/learn?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");

        try (Connection con = dataSource.getConnection()) {
            // 利用 con 对象执行 SQL,建立连接
            String sql = "select * from oj_records where difficulty = ?";
            // 动态 SQL
            try (PreparedStatement ps = con.prepareStatement(sql)) {
                ps.setString(1, difficulty);

                System.out.println(ps);
                try (ResultSet rs = ps.executeQuery()) {
                    // 结果就可以从 ResultSet 对象中获取

                    // hasNext() + next()
                    while (rs.next()) {
                        // rs 代表当前遍历的行
                        // 第一列的结果是表的名称,所以是字符串 String
                        String id = rs.getString(1);    // 下标是 1
                        System.out.println(id);
                    }
                }
            }
        }
    }
}

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&amp;MySQL,数据库,mysql,sql

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&amp;MySQL,数据库,mysql,sql

3.2 SQL 类型
// 针对 PreparedStatement 执行动态绑定时
int x = ...;
ps.setInt(1, x);

String s = "...";
ps.setString(1, s);

String datetime = "2023-04-24 20:54:00"
ps.setString(1, datetime);


// 从 ResultSet 获取值时
int x = rs.getInt(1);
String s = rs.getString(1);
String datetime = rs.getString(1);
Java 代码类型 MySQL 类型 举例

int

setInt(..)

getInt(..)

int

ps.getInt(1,x);

x = rs.getInt(1);

String

varchar(..)

char(..)

text

longtext

String

datetime

date

time

3.3 SQL 注入(Inject)

什么是 SQL 注入呢?

SQL注入是一种非常常见的数据库攻击手段,SQL注入漏洞也是网络世界中最普遍的漏洞之一。大家也许都听过某某学长通过攻击学校数据库修改自己成绩的事情,这些学长们一般用的就是SQL注入方法。

SQL注入其实就是恶意用户通过在表单中填写包含SQL关键字的数据来使数据库执行非常规代码的过程。,SQL数据库的操作是通过SQL语句来执行的,而无论是执行代码还是数据项都必须写在SQL语句之中,这就导致如果我们在数据项中加入了某些SQL语句关键字(比如说SELECT、DROP等等),这些关键字就很可能在数据库写入或读取数据时得到执行。

我们来从代码角度看看是如何进行 SQL 注入的。

String username = scanner.nextLine();

String sql = "select * from users where username = '%s'";
sql = String.format(sql, username);

// 执行

当用户输入用户名是类型 " ' or 1 = 1 or 1 = ' "

String sql = "select * from users where username = '' or 1 = 1 or 1 = ''";

由于 name = ' ' 为假,1 = 1 为真,1 = ' ' 为假,因此 where 后面的语句最终执行结果为真,因此,会将数据库中的所有信息显示出来,从而导致数据泄露。

4. 关于 SQL 的执行

4.1 带查询结果的执行
select ...;
-- 查询..
show ...;
-- 显示..
ResultSet rs = ps.executeQuery();
4.2 不带查询结果的执行
create database ...;
-- 创建库
create table ...;
-- 创建表
drop database ...;
-- 删除库
drop table ...;
-- 删除表
insert into ...;
-- 增加数据
update ...;
-- 更新数据
delete ...;
-- 删除数据

// number 本次执行成功多少行
int number = ps.executeUpdate();
4.3 关于 ResultSet 读取列
1)读取第 n 列
rs.getInt(n);    // 读取第 n 列

2) 根据列名称读取
rs.getInt("oj_id");

5. 实践应用:博客系统

只有一张表          博客文章(blogs)

0) bid    int   PK  AI
1) author varchar(30)  NN         作者
2) published_at datetime NN       发表时间
3) content text NN                正文 

create table blogs (
    bid int primary key auto_increment,
    author varchar(20) not null comment '作者',
    title varchar(100) not null comment '标题',
    published_at datetime not null comment '发表时间',
    content text not null comment '正文'
);
-- 新增文章
insert into blogs (author, title, published_at, content) values (?, ?, ?, ?);

-- 删除一篇文章
   -- by bid
delete from blogs where bid = ?;
   -- 批量 by bid
delete from blogs where bid in (...);
   -- 根据标题(只能删自己的)
delete from blogs where author = ? and title = ?;

-- 给出文章列表(bid/作者/发表时间/标题)
-- 根据发表从新到旧
select bid, author, published_at, title from blogs order by published_at desc;
-- 增加分页(每页共 3 篇),需要第 x 页
select bid, author, published_at, title from blogs order by published_at desc limit 3 offset 3 * (x - 1)
x == 1 : offset 0
x == 2 : offset 3
x == 3 : offset 6

创建一个 DBUtil 类用来存放配置信息:

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

// Util: 工具
public class DBUtil {
    private static final DataSource dataSource;

    static {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();

        mysqlDataSource.setUser("debian-sys-maint");
        mysqlDataSource.setPassword("***********");
        mysqlDataSource.setUrl("jdbc:mysql://127.0.0.1:3306/learn?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai");
        dataSource = mysqlDataSource;
    }

    public static Connection connection() throws SQLException {
        return dataSource.getConnection();
    }
}
发表文章

接下来实现发表文章的功能:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Scanner;

public class PublishArticle {
    public static void main(String[] args) throws SQLException {
        Scanner sc = new Scanner(System.in);

        System.out.println("请输入用户名:");
        String author = sc.nextLine();

        System.out.println("请输入文章标题:");
        String title = sc.nextLine();

        System.out.println("请输入正文内容:");
        String content = sc.nextLine();
        // 获取当前时间
        ZoneId zone = ZoneId.of("Asia/Shanghai");
        LocalDateTime now = LocalDateTime.now(zone);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String publishedAt = now.format(formatter);

        String sql = "insert into blogs (author, title, published_at, content) values (?, ?, ?, ?)";

        try (Connection c = DBUtil.connection()) {
            try (PreparedStatement ps = c.prepareStatement(sql)) {
                // 1. 进行动态绑定
                ps.setString(1, author);
                ps.setString(2, title);
                ps.setString(3, publishedAt);
                ps.setString(4, content);

                // 此处是不带结果集的查询
                System.out.println(ps);
                ps.executeUpdate();

                System.out.println("文章发表成功");
            }
        }
    }
}

5. MySQL - JDBC & SQL 注入 &博客系统(万字详解),数据结构&amp;MySQL,数据库,mysql,sql文章来源地址https://www.toymoban.com/news/detail-599112.html

删除文章ByBid
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

public class DeleteByBid {
    public static void main(String[] args) throws SQLException {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入要删除的 bid: ");
        int bid = scanner.nextInt();

        String sql = "delete from blogs where bid = ?";

        try (Connection c = DBUtil.connection()) {
            try (PreparedStatement ps = c.prepareStatement(sql)) {
                ps.setInt(1, bid);

                System.out.println(ps);

                ps.executeUpdate();

                System.out.println("删除成功");
            }
        }
    }
}
批量删除文章ByBid
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Collectors;

public class BulkDeleteByBid {
    public static void main(String[] args) throws SQLException {
        Scanner scanner = new Scanner(System.in);
        List<Integer> bidList = new ArrayList<>();
        System.out.print("请输入要删除的 bid: ");
        while (scanner.hasNextInt()) {
            int bid = scanner.nextInt();
            bidList.add(bid);
            System.out.print("请输入要删除的 bid: ");
        }

        System.out.println("DEBUG: 要删除的 bid 列表为: " + bidList);
        // bidList -> "1, 3, 7
        // List<Integer> -> List<String>
        // String.join(", ", list of string)
        // Stream 流式写法
        List<String> bidListString = bidList.stream()
                .map(i -> String.valueOf(i))
                .collect(Collectors.toList());
        String inClause = String.join(", ", bidListString);

        String sql = String.format("delete from blogs where bid in (%s)", inClause);

        try (Connection c = DBUtil.connection()) {
            try (PreparedStatement ps = c.prepareStatement(sql)) {

                System.out.println(ps);

                ps.executeUpdate();

                System.out.println("批量删除成功");
            }
        }
    }
}

到了这里,关于5. MySQL - JDBC & SQL 注入 &博客系统(万字详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL最终弹-并发(脏读,不可重复读,幻读及区别),JDBC的使用和安装,最全万字

    一、💛并发基本概念   并发的基本意思: 什么是并发呢?简单的理解就是同一时间执行 服务器同一时刻,给多个客户端提供服务~~,这两个客户端都可以给服务器提交事务。 如果提交两个事务,改不同的表还没啥影响,假如要改相同的表,这时候可能会出现麻烦。 二、

    2024年02月14日
    浏览(43)
  • 万字讲解9种Web应用攻击与防护安全。XSS、CSRF、SQL注入等是如何实现的

    OWASP(开放Web软体安全项目- Open Web Application Security Project) 是一个开源的、非盈利的全球性安全组织,致力于应用软件的安全研究。使命 是使应用软件更加安全,使企业和组织能够对应用安全风险做出更清晰的决策。 http://www.owasp.org.cn/ OWASP在业界影响力: OWASP被视为web应用

    2023年04月15日
    浏览(54)
  • 【数据结构】排序合集(万字详解)

    排序,以字面意思来说就是通过特定的算法将一组或多组无序或者接近有序的数据,以升序或者降序的方式重新进行排序组合; [7,4,2,9,8,6,5,1,3]; 以升序的方式进行排序最终为: [1,2,3,4,5,6,7,8,9]; 排序算法就是如何使得数据按照要求排列的方法; 排序的算法多种多样,基本的排序

    2024年02月08日
    浏览(40)
  • 【Node.js实战】一文带你开发博客项目之安全(sql注入、xss攻击、md5加密算法)

    个人简介 👀 个人主页: 前端杂货铺 🙋‍♂️ 学习方向: 主攻前端方向,也会涉及到服务端 📃 个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀 未来打算: 为中国的工业软件事业效力n年 🥇 推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2Vue3项目实战 🥝

    2024年02月03日
    浏览(52)
  • 【高阶数据结构】手撕哈希表(万字详解)

    (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是 Scort 目前状态:大三非科班啃C++中 🌍博客主页:张小姐的猫~江湖背景 快上车🚘,握好方向盘跟我有一起打天下嘞! 送给自己的一句鸡汤🤔: 🔥真正的大师永远怀着一颗学徒的心 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏 🎉🎉

    2024年01月19日
    浏览(52)
  • SQL注入之MYSQL注入

    前言(菜鸡教程大佬勿喷) MYSQL注入中首先要明确当前注入点权限,高权限注入时有更多的攻击手法,有的能直接进行getshell操作。其中也会遇到很多阻碍,相关防御方案也要明确,所谓知己知彼,百战不殆。不论作为攻击还是防御都需要了解其中的手法和原理,这样才是一个

    2024年02月05日
    浏览(50)
  • 数据结构:树和二叉树之-堆排列 (万字详解)

    目录 树概念及结构 1.1树的概念 1.2树的表示 ​编辑2.二叉树概念及结构 2.1概念 2.2数据结构中的二叉树:​编辑 2.3特殊的二叉树: ​编辑 2.4 二叉树的存储结构 2.4.1 顺序存储: 2.4.2 链式存储: 二叉树的实现及大小堆排列 1功能展示 2 定义基本结构 3 初始化 4打印 5销毁 6插入

    2024年02月07日
    浏览(40)
  • 【数据结构】万字超详解顺序表(比细狗还细)

    我这个人走得很慢,但是我从不后退。                                ——亚伯拉罕·林肯   目录 一.什么是线性表? 二.什么是顺序表? 三.接口函数的实现 1.创建工程 2.构造顺序表 3.初始化顺序表 3.初始化顺序表 4.顺序表的尾插 5.顺序表的头插  6.顺序表的尾删  7.顺序

    2023年04月09日
    浏览(41)
  • 【数据结构篇】线性表1 --- 顺序表、链表 (万字详解!!)

    目录  顺序表(ArrayList) 什么是顺序表?  代码实现 (MyArrayList) --- 打印顺序表  --- 新增元素  1.新增元素,默认在数组最后新增 2.在指定位置新增元素  --- 判断是否包含某个元素 --- 查找某个元素具体位置(下标) --- 获取 pos 位置的元素 --- 给pos位置 的值设为 value  -

    2024年02月10日
    浏览(33)
  • 数据结构:线性表之-循环双向链表(万字详解)

    目录 基本概念 1,什么是双向链表 2,与单向链表的区别 双向链表详解 功能展示: 1. 定义链表 2,创建双向链表 3,初始化链表 4,尾插 5,头插 6,尾删 判断链表是否被删空 尾删代码 7,头删 8,pos位置之前插入 优化后的头插 优化后的尾插 9,删除pos位置的节点 优化后的尾删 优

    2024年02月09日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包