go语言连接mysql、sqlx、sql注入

文章目录

  • 前言
  • 一、连接mysql常用到的方法?
    • Open("数据库类型","用户名:密码@/数据库")
    • Exec()
    • Query()
    • QueryRow()
    • Prepare()
  • 二、操作mysql
    • 1.引入包
    • 2.建立连接
    • 3.插入数据
    • 4.修改数据
    • 5.删除数据
    • 6.查询数据
  • 三、使用sqlx进行效率的提高,以及方式sql注入攻击
    • ①使用sqlx简化操作
    • ②sql注入简介
      • (1)sql注入产生根本原因
      • (2)模拟sql注入
  • 总结
  • GO GO GO !


前言

无论做什么都需要进行数据的增删改查,而进行增删改查的时候使用文件虽操作方便,但是数据格式化要求不强,使用数据库可以规范的操作数据


一、连接mysql常用到的方法?

Open(“数据库类型”,“用户名:密码@/数据库”)

	功能:操作成功返回一个DB对象,DB对象用于多线程是安全的,db对象内部封装了连池
	底层实现:open函数并没有创建连接,他只是验证参数是否合法,然后开启一个单独
	的线程去监听是否需要建立一个新连接,当有请求建立新连接时,就创建新连接

Exec()

执行不返回行的查询,比如插入修改删除
		DB交给内部的exec方法负责查询,exec首先会调用db内部的conn方法,从连接池中获得一个连接
		然后检查内部的driver.Conn实现了Execerl实现了接口没有,如果实现了接口就会调用,Execer接
		口的Exec方法执行查询,否则调用Conn接口的Prepare方法进行查询。

Query()

	功能:用于检索,比如select
	实现:db先调用内部的query方法负责查询,query首先调用db内部的conn方法从连接池内获得
	一个新连接,然后调用queryConn方法负责查询

QueryRow()

	功能:用于返回单行的查询
	实现:接收到参数转交给db.Query()

Prepare()

		功能:返回一个stmt,使用stmt对象可以进行上面的三个操作
		实现:db交给内部的preapre方法负责查询,preapre首先从连接池中获取一个连接
			 然后调用driverConn的prepareLocked方法负责查询。
	db.Begin()开启事务返回Tx对象,调用该方法之后,tx与该指定的来连接就绑定到一起了,
	一旦事物进行提交或者回滚该事务,绑定的连接就进行解绑
		tx.Exec()
		tx.Query()
		tx.QueryRow()
		tx.Prepare()
		tx.Commit()
		tx.Rollback()

二、操作mysql

1.引入包

代码如下(示例):

	_ "database/sql"
	"fmt"
		//这里如果包没有办法自动下载就去github上进行下载然后放在src目录下,前面导包的时候有介绍
	_ "github.com/go-sql-driver/mysql"

2.建立连接

代码如下(示例):

	// 对错误的封装
	func printErr(e error) {
		if e != nil {
			fmt.Println(e)
		}
	}
	//open传进去的是数据库配置信息,登录用户密码还有主机地址端口号,map是连接的数据库名,以及编码方式
	db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8")
	printErr(err)

3.插入数据

代码如下(示例):

package main

import (
	"database/sql"
	_ "database/sql"
	"fmt"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
	// _"mysql"
)

func printErr(err error) {
	if err != nil {
		fmt.Println("出错了:", err)
	}
}

func main() {
	// 进行数据库初始化
	db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8")
	printErr(err)
	//这个是延时关闭
	defer db.Close()
	// 使用该方法进行单个信息插入还好,不适用于多次插入(因为每次插入建立了一个新连接效率慢)
	db.Exec("insert into test(name,age) values(?,?)", "荷花66", 66)
	db.Exec("insert into test(name,age) values(?,?)", "荷花池", 12)

	// 以下进行事务的提交,期间只进行一次连接
	// 开启一段事物
	tx, err := db.Begin()
	printErr(err)
	for i := 0; i < 30; i++ {
		tx.Exec("insert into test(name,age) values(?,?)", "name"+strconv.Itoa(i), i)
	}
	// 进行事物的提交
	tx.Commit()
	fmt.Println("213")
}

4.修改数据

代码如下(示例):

package main

import (
	_ "database/sql"
	"fmt"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
)

func printErr(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
func main() {
	db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1)/map?charset=utf8")
	printErr(err)
	defer db.Close()
	db.Exec("update test set age=? where name=?", strconv.Itoa(199), "荷花池")
	db.Exec("update test set name=? where name=?", strconv.Itoa(199), "荷花66")
	fmt.Println("213")
}

5.删除数据

代码如下(示例):

package main

import (
	"database/sql"
	"fmt"
	"strconv"

	_ "github.com/go-sql-driver/mysql"
	// _"mysql"
)

func printErr(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
func main() {
	db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1)/map?charset=utf8")
	printErr(err)
	defer db.Close()
	tx, _ := db.Begin()
	for i := 0; i < 30; i++ {
		_, err := tx.Exec("delete from test where age=?", strconv.Itoa(i))
		printErr(err)
	}
	tx.Commit()
	//使用Exec进行删除,会将所有满足的数据均删除掉
	db.Exec("delete from test where age=?", strconv.Itoa(199))
	fmt.Println("213")
}

6.查询数据

代码如下(示例):

	// 测试是否连接成功
	row, err := db.Query("select * from schoolmp where place=1000")
	// fmt.Println()
	printErr(err)
	i := 0
	//迭代数据库每一条数据(这个类似于一个迭代器)
	for row.Next() {
		var temp map_node
		//读取数据
		err = row.Scan(&temp.place)
		//自己封装的错误打印函数
		printErr(err)
		fmt.Println(temp)
		i++
	}
	// 可以用于判断查询的数据一共有多少条
	if i == 0 {
		fmt.Println("没有数据!")
	} else {
		fmt.Println("数据一共有", i, "条")
	}

三、使用sqlx进行效率的提高,以及方式sql注入攻击

①使用sqlx简化操作

sqlx最明显的操作是,可以对结构体对象直接进行操作,将数据库内的数据
直接读到结构体对象内
 package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
)

// 存储查询到的信息
type PName struct {
	Name1 string
	Name2 string
	Worth int
}

var db *sqlx.DB

func initDB() (err error) {
	dsn := "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8mb4&parseTime=True"
	// 也可以使用MustConnect连接不成功就panic
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	// 最大打开的连接数
	db.SetMaxOpenConns(20)
	// 设置闲置的连接数
	db.SetMaxIdleConns(10)
	return
}

// 查询单条数据
func selectOne() {
	var p PName
	sql := "select name1,name2,worth from schoolmp_atb where name1 = ?;"
	err := db.Get(&p, sql, "大门")
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(p)

}

// 查询所有数据
func selectMany() {
	var p []PName
	sql := "select name1,name2,worth from schoolmp_atb;"
	_ = db.Select(&p, sql)
	for index, temp := range p {
		fmt.Println(index, temp)
	}
}

// 实现批量插入
// BatchInsertUsers2 使用sqlx.In帮我们拼接语句和参数, 注意传入的参数是[]interface{}
func BatchInsertUsers2(users []interface{}) error {
	query, args, _ := sqlx.In(
		"INSERT INTO user (name, age) VALUES (?), (?), (?)",
		users..., // 如果arg实现了 driver.Valuer, sqlx.In 会通过调用 Value()来展开它
	)
	fmt.Println(query) // 查看生成的querystring
	fmt.Println(args)  // 查看生成的args
	_, err := db.Exec(query, args...)
	return err
}
func main() {
	err := initDB()
	if err != nil {
		fmt.Println("建立连接失败!", err)
		return
	}
	fmt.Println("连接成功!")
	// selectOne()
	selectMany()
}

②sql注入简介

(1)sql注入产生根本原因

	执行sql命令的时候,可以对sql命令进行拼接,导致所有信息泄露,所以应该操作的时候不可以
	直接对数据库进行操作,应该先对存储数据的集合或者链表进行操作

(2)模拟sql注入

 package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jmoiron/sqlx"
	_ "github.com/jmoiron/sqlx"
)

var db *sqlx.DB

/*sql注入就是利用sql语言拼接的bug进行数据的批量获取*/
func initDB() (err error) {
	dsn := "root:123456@tcp(127.0.0.1:3306)/map?charset=utf8mb4&parseTime=True"
	// 也可以使用MustConnect连接不成功就panic
	db, err = sqlx.Connect("mysql", dsn)
	if err != nil {
		fmt.Printf("connect DB failed, err:%v\n", err)
		return
	}
	// 最大打开的连接数
	db.SetMaxOpenConns(20)
	// 设置闲置的连接数
	db.SetMaxIdleConns(10)
	return
}

type PName struct {
	Name1 string
	Name2 string
	Worth int
}

func selectData(name string) {
	var p []PName
	sql := "select name1,name2,worth from schoolmp_atb where name1 = %s ;"
	sqlStr := fmt.Sprintf(sql, name)
	err := db.Select(&p, sqlStr)
	if err != nil {
		fmt.Println(err)
		return
	}
	for index, temp := range p {
		fmt.Println(index, temp)
	}
	return
}
func main() {
	_ = initDB()
	// #在select中起到的是注释的作用
	// 将所有数据查询出来
	 selectData("'xxx' or 1=1#")
	// 将大致数据统计出来(如果数据大于10个则将数据统计出来)
	selectData("'xxx' or (select count(*) from  schoolmp_atb)>100#")

	// selectData("'大门'")

	fmt.Println(123)
}
		

	上述案例虽然是执行的xxx操作语句,但是打印结果是
	0 {大门 操场 90}
	1 {大门 图书馆 60}        
	2 {大门 6号宿舍 100}      
	3 {图书馆 操场 20}        
	4 {图书馆 6号宿舍 50}     
	5 {操场 小礼堂 65}        
	6 {图书馆 小礼堂 40}      
	7 {小礼堂 下沉广场 55}    
	8 {6号宿舍 下沉广场 80}   
	9 {下沉广场 学宛餐厅 50}  
	10 {下沉广场 专家公寓 100}
	11 {学宛餐厅 专家公寓 130}
	12 {专家公寓 教工3村 150}
	13 {操场 教工3村 250}
	14 {教工3村 荷花池 100}
	15 {荷花池 9#教学楼 40}
	16 {荷花池 12#教学楼 60}
	17 {9#教学楼 12#教学楼 30}
	18 {9#教学楼 学生广场 75}
	19 {12#教学楼 学生广场 15}
	20 {12#教学楼 15#教学楼 50}
	21 {15#教学楼 学生广场 45}
	22 {15#教学楼 网球场 160}
	23 {学生广场 网球场 100}
	24 {网球场 东南餐厅 120}
	25 {北京 南京 2000}
	26 {1 1 1}
	27 {123 123 123}
	28 {1 1 1}
	123
	//将所有的数据查询了出来
	//也可以使用
	"'xxx' or (select count(*) from  schoolmp_atb)>100#"
	语句进行数据量的范围估计(其中100是估算的数量范围)

总结

数据库操作较为简单,但是数据库安全尤为重要,虽然现在市面上许多公司完成了数据库一些简单的防控,但是我们仍然不可以掉以轻心。了解其原理是解决问题的关键。


go语言连接mysql、sqlx、sql注入_第1张图片


GO GO GO !

你可能感兴趣的:(Go语言从入门到精通,mysql,sql,golang)