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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: go语言连接mysql、sqlx、sql注入

文章目录



前言


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



一、连接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是估算的数量范围)


总结


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

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
11天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
30 2
|
9天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
20 2
|
9天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
22 2
|
13天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
88 58
|
1天前
|
安全 Go 数据处理
Go语言中的并发编程:掌握goroutine和channel的艺术####
本文深入探讨了Go语言在并发编程领域的核心概念——goroutine与channel。不同于传统的单线程执行模式,Go通过轻量级的goroutine实现了高效的并发处理,而channel作为goroutines之间通信的桥梁,确保了数据传递的安全性与高效性。文章首先简述了goroutine的基本特性及其创建方法,随后详细解析了channel的类型、操作以及它们如何协同工作以构建健壮的并发应用。此外,还介绍了select语句在多路复用中的应用,以及如何利用WaitGroup等待一组goroutine完成。最后,通过一个实际案例展示了如何在Go中设计并实现一个简单的并发程序,旨在帮助读者理解并掌
|
4天前
|
Go API 数据库
Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
本文介绍了 Go 语言中常用的 ORM 框架,如 GORM、XORM 和 BeeORM,分析了它们的特点、优势及不足,并从功能特性、性能表现、易用性和社区活跃度等方面进行了比较,旨在帮助开发者根据项目需求选择合适的 ORM 框架。
18 4
|
4天前
|
缓存 监控 前端开发
在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统
本文深入探讨了在 Go 语言中实现 WebSocket 实时通信的应用,包括 WebSocket 的简介、Go 语言的优势、基本实现步骤、应用案例、注意事项及性能优化策略,旨在帮助开发者构建高效稳定的实时通信系统。
32 1
|
7天前
|
Go
go语言中的continue 语句
go语言中的continue 语句
18 3
|
12天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
8天前
|
安全 Go 调度
探索Go语言的并发模型:goroutine与channel
在这个快节奏的技术世界中,Go语言以其简洁的并发模型脱颖而出。本文将带你深入了解Go语言的goroutine和channel,这两个核心特性如何协同工作,以实现高效、简洁的并发编程。