gin框架提高篇(四)

参数校验(一)

uuid包:https://github.com/satori/go.uuid
因为作者更改了参数限制,导致会出问题 → 问题解决

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	uuid "github.com/gofrs/uuid"
	"net/http"
	"unicode/utf8"
)

type UserInfo struct {
	Id   string `validate:"uuid" json:"id"`           // UUID 类型
	Name string `validate:"checkName" json:"name"`    // 自定义校验
	Age  uint8  `validate:"min=0,max=130" json:"age"` // 大于0小于130
}

var validate *validator.Validate

// 校验初始化
func init() {
	validate = validator.New()                              // 初始化校验示例
	validate.RegisterValidation("checkName", checkNameFunc) // 自定义校验方法
}

func checkNameFunc(fl validator.FieldLevel) bool {
	count := utf8.RuneCountInString(fl.Field().String()) // 获取 Name 的字符串表示,并计算 Unicode 字符的数量
	if count >= 2 && count <= 12 {
		return true
	}
	return false
}

func main() {
	uuid.Must(uuid.NewV4()) // 由 uuid包 生成 uuid

	r := gin.Default()
	var user UserInfo
	r.POST("/validate", func(context *gin.Context) {
		err := context.Bind(&user)
		if err != nil {
			context.JSON(http.StatusBadRequest, "请求参数错误")
			return
		}
		err = validate.Struct(user)
		if err != nil {
			for _, e := range err.(validator.ValidationErrors) {
				fmt.Println("错误的字段:", e.Field())
				fmt.Println("错误的值:", e.Value())
				fmt.Println("错误的tag:", e.Tag())
			}
			context.JSON(http.StatusBadRequest, "数据校验失败")
			return
		}
		context.JSON(http.StatusOK, "数据校验成功")
	})
	r.Run(":9090")
}

在这里插入图片描述

参数校验(二)

validator包:https://github.com/go-playground/validator

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"net/http"
)

type ValUser struct {
	Name  string `validate:"required" json:"name"`
	Age   uint8  `validate:"gte=0,lte=130" json:"age"`
	Email string `validate:"required,email" json:"email"`
	// 切片数据类型
	Address []ValAddress `validate:"dive" json:"address"`
}

type ValAddress struct {
	Province string `validate:"required" json:"province"`
	City     string `validate:"required" json:"city"`
	Phone    string `validate:"numeric,len=11" json:"phone"`
}

var validate *validator.Validate

func init() {
	validate = validator.New() // 初始化
}

func main() {
	r := gin.Default()
	var user ValUser
	r.POST("/validate", func(context *gin.Context) {
		//testData(context)

		err := context.Bind(&user)
		if err != nil {
			context.JSON(http.StatusBadRequest, "参数错误,绑定失败")
			return
		}
		// 参数校验
		if validateUser(user) {
			context.JSON(http.StatusOK, "数据校验成功")
			return
		}
		context.JSON(http.StatusBadRequest, "校验失败")
		return
	})
	r.Run(":9090")
}

//func testData(context *gin.Context) {
//	address := ValAddress{
//		Province: "浙江省",
//		City:     "杭州市",
//		Phone:    "13575121689",
//	}
//	user := ValUser{
//		Name:    "张三",
//		Age:     15,
//		Email:   "1993036922@qq.com",
//		Address: []ValAddress{address},
//	}
//	context.JSON(http.StatusOK, user)
//}

func validateUser(u ValUser) bool {
	err := validate.Struct(u)
	if err != nil {
		for _, e := range err.(validator.ValidationErrors) {
			fmt.Println("错误的字段:", e.Field())
			fmt.Println("错误的值:", e.Value())
			fmt.Println("错误的tag:", e.Tag())
		}
		return false
	}
	return true
}

在这里插入图片描述

swagger

swagger地址:https://github.com/swaggo/gin-swagger

引入步骤

  • 下载swag及相关包
    • go get -u github.com/swaggo/swag/cmd/swag
    • go get -u github.com/swaggo/gin-swagger
    • go get -u github.com/swaggo/files
  • 实现 Api 代码
  • 编写注释:参考https://github.com/swaggo/swag/blob/master/README.md#declarative-comments-format
  • 在代码中使用 swagger 中间件
  • 引入docs:如我的引入为 _ “2-golang/docs”,_ 表示不为包赋名
  • 执行终端命令:swag init

如果出现 swag 不是命令,无法识别,则先执行: go install github.com/swaggo/swag/cmd/swag@latest

ps:文件名必须叫 main

package main

import (
	_ "2-golang/docs"
	"fmt"
	"github.com/gin-gonic/gin"
	swaggerFiles "github.com/swaggo/files"
	ginSwagger "github.com/swaggo/gin-swagger"
	"net/http"
)

type User struct {
	UserName string `json:"user_name"`
	Password string `json:"password"`
}

type Response struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
	Data string `json:"data"`
}

func main() {
	r := gin.Default()
	// 使用 swagger 中间件
	r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))

	r.GET("/login", login)

	r.POST("/register", register)

	r.Run(":9090")
}

// @Tags 注册接口
// @Summary 注册
// @Description register
// @Accept json
// @Produce json
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Success 200 {string} json "{"code": 200, "data":"{"name":"username","password":"password"}","msg":"OK"}"
// @Router /register [post]
func register(context *gin.Context) {
	var user User
	// get 使用的是 query,post 使用的是 formData,而 Bind 只能绑定 query
	// err := context.Bind(&user)
	err := context.BindQuery(&user)

	if err != nil {
		context.JSON(http.StatusBadRequest, "数据错误")
		return
	}

	res := Response{
		Code: http.StatusOK,
		Msg:  "注册成功",
		Data: "OK",
	}

	context.JSON(http.StatusOK, res)
}

// @Tags 登录接口
// @Summary 登录
// @Description login
// @Accept json
// @Produce json
// @Param username query string true "用户名"
// @Param password query string false "密码"
// @Success 200 {string} json "{"code": 200, "data":"{"name":"username","password":"password"}","msg":"OK"}"
// @Router /login [get]
func login(context *gin.Context) {
	userName := context.Query("name")
	pwd := context.Query("pwd")

	fmt.Println(userName, pwd)

	res := Response{}
	res.Code = http.StatusOK
	res.Msg = "登陆成功"
	res.Data = "OK"

	context.JSON(http.StatusOK, res)
}

在这里插入图片描述

gin框架cookie

package main

import (
	"encoding/hex"
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

var cookieName string
var cookieValue string

func main() {
	r := gin.Default()

	r.Use(cookieAuth())

	r.GET("/cookie", func(context *gin.Context) {
		name := context.Query("name")

		if len(name) <= 0 {
			context.JSON(http.StatusOK, "数据错误")
			return
		}

		cookieName = "cookie_" + name
		cookieValue = hex.EncodeToString([]byte(cookieName + "value"))
		val, _ := context.Cookie(cookieName)

		if val == "" {
			context.String(http.StatusOK, "Cookie已经下发,下次登录有效", cookieName)
			return
		}
		context.String(http.StatusOK, "验证成功,cookie值为:%s", val)
	})

	r.Run(":9090")
}

func cookieAuth() gin.HandlerFunc {
	return func(context *gin.Context) {
		val, _ := context.Cookie(cookieName)

		if val == "" {
			context.SetCookie(cookieName, cookieValue, 3600, "/", "localhost", true, true)
			fmt.Println("cookie已经保存完成!")
		}
	}
}

在这里插入图片描述

gin框架session

package main

import (
	"net/http"

	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/cookie"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	// 加入 session 中间件
	store := cookie.NewStore([]byte("session_secret")) // 创建基于cookie的session存储
	r.Use(sessions.Sessions("mySession", store))       // 使用session中间件,并指定session名称和存储方式

	r.GET("/session", func(context *gin.Context) {
		name := context.Query("name")
		if len(name) <= 0 {
			context.JSON(http.StatusOK, "数据错误")
			return
		}

		sessionName := "session_" + name        // 构造session名称
		sessionValue := "session_value_" + name // 构造session值

		session := sessions.Default(context) // 获取默认的session实例
		sessionData := session.Get(sessionName)

		if sessionData != sessionValue { // 如果session中不存在对应值,则为首次访问
			session.Set(sessionName, sessionValue) // 设置 session
			session.Save()                         // 保存 session

			context.JSON(http.StatusOK, "首次访问,session已保存:"+sessionValue)
			return
		}
		context.JSON(http.StatusOK, "访问成功,您的session是:"+sessionData.(string))
	})

	r.Run(":9090") // 启动HTTP服务器,监听9090端口
}

gin框架Https

在这里插入图片描述

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/unrolled/secure"
	"net/http"
)

// HttpRes 结构体用于定义HTTP响应格式
type HttpRes struct {
	Code   int    `json:"code"`   // 响应状态码
	Result string `json:"result"` // 响应结果消息
}

func main() {
	r := gin.Default() // 创建默认的gin路由引擎

	// 使用HTTPS处理程序中间件
	r.Use(httpsHandler())

	// 定义GET路由"/https_test"
	r.GET("/https_test", func(context *gin.Context) {
		// 返回JSON格式的成功消息
		context.JSON(http.StatusOK, HttpRes{
			Code:   http.StatusOK,
			Result: "测试成功",
		})
	})

	path := "D:/2-golang/CA/"                       // 证书路径
	r.RunTLS(":9090", path+"ca.crt", path+"ca.key") // 使用TLS在9090端口运行服务器
}

// httpsHandler 返回一个处理HTTPS请求的中间件函数
func httpsHandler() gin.HandlerFunc {
	return func(context *gin.Context) {
		// 创建secure中间件实例
		secureMiddleware := secure.New(secure.Options{
			SSLRedirect:          true, // 强制SSL重定向
			STSSeconds:           1536000,
			STSIncludeSubdomains: true,
			STSPreload:           true,
			FrameDeny:            true,
			ContentTypeNosniff:   true,
			BrowserXssFilter:     true,
		})
		// 处理HTTPS请求
		err := secureMiddleware.Process(context.Writer, context.Request)
		if err != nil {
			// 如果出现错误,返回数据不安全的响应
			context.AbortWithStatusJSON(http.StatusBadRequest, "数据不安全")
			return
		}
		if status := context.Writer.Status(); status > 300 && status < 399 {
			// 如果响应状态码是重定向类型,则终止请求
			context.Abort()
			return
		}
		context.Next() // 继续处理下一个中间件或路由处理函数
	}
}

在这里插入图片描述

在这里插入图片描述

安全认证(一 静态方法)

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/pjebs/restgate"
	"net/http"
)

func main() {
	r := gin.Default()

	// 使用自定义的认证中间件
	r.Use(authMiddleware())

	// 定义一个需要认证的路由
	r.GET("/auth1", func(context *gin.Context) {
		// 返回验证通过的JSON响应
		resData := struct {
			Code int         `json:"code"`
			Msg  string      `json:"msg"`
			Data interface{} `json:"data"`
		}{http.StatusOK, "验证通过", "OK"}

		context.JSON(http.StatusOK, resData)
	})

	// 启动HTTP服务器并监听9090端口
	r.Run(":9090")
}

// authMiddleware 是一个自定义的 Gin 中间件,用于身份验证
func authMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		// 创建一个 restgate 实例用于验证
		gate := restgate.New(
			"X-Auth-Key",
			"X-Auth-Secret",
			restgate.Static,
			restgate.Config{
				Key:                []string{"admin"},
				Secret:             []string{"adminpw"},
				HTTPSProtectionOff: true,
			},
		)
		
		// 定义一个变量来标记是否调用了下一个中间件
		nextCalled := false
		
		// 定义一个适配器函数,用于在调用下一个中间件时设置标记
		nextAdapter := func(http.ResponseWriter, *http.Request) {
			nextCalled = true
			context.Next()
		}
		
		// 使用 restgate 实例来验证请求,并传入适配器函数
		gate.ServeHTTP(context.Writer, context.Request, nextAdapter)
		
		// 如果没有调用下一个中间件,则返回未经授权的状态码
		if !nextCalled {
			context.AbortWithStatus(http.StatusUnauthorized)
		}
	}
}

在这里插入图片描述

产生警告:WARNING: HTTPS Protection is off. This is potentially insecure!
解决方案:按照 https 流程完成即可

安全认证(二 数据库方法)

package main

import (
	"database/sql"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"github.com/pjebs/restgate"
	"net/http"
)

// main 函数是程序的入口点
func main() {
	// 创建一个默认的 Gin 引擎实例
	r := gin.Default()

	// 使用自定义的认证中间件
	r.Use(authMiddleware())

	// 定义一个需要认证的路由
	r.GET("/auth2", func(context *gin.Context) {
		// 返回验证通过的JSON响应
		resData := struct {
			Code int    `json:"code"`
			Msg  string `json:"msg"`
			Data string `json:"data"`
		}{http.StatusOK, "验证通过", "OK"}

		context.JSON(http.StatusOK, resData)
	})

	// 启动HTTP服务器并监听9090端口
	r.Run(":9090")
}

var db *sql.DB

// init 函数用于初始化数据库连接
func init() {
	// 初始化数据库连接
	db, _ = SqlDB()
}

// SqlDB 函数用于连接数据库
func SqlDB() (*sql.DB, error) {
	// 定义数据库连接参数
	DB_TYPE := "mysql"
	DB_HOST := "localhost"
	DB_PORT := "3306"
	DB_USER := "root"
	DB_NAME := "api_secure"
	DB_PASSWORD := "123456"
	openString := DB_USER + ":" + DB_PASSWORD + "@tcp(" + DB_HOST + ":" + DB_PORT + ")/" + DB_NAME

	// 打开数据库连接
	db, err := sql.Open(DB_TYPE, openString)
	return db, err
}

// authMiddleware 是一个自定义的 Gin 中间件,用于身份验证
func authMiddleware() gin.HandlerFunc {
	return func(context *gin.Context) {
		// 创建一个 restgate 实例用于验证
		gate := restgate.New(
			"X-Auth-Key",
			"X-Auth-Secret",
			restgate.Database,
			restgate.Config{
				DB:                 db,
				Key:                []string{"keys"},
				Secret:             []string{"secrets"},
				TableName:          "users",
				HTTPSProtectionOff: true,
			},
		)

		// 定义一个变量来标记是否调用了下一个中间件
		nextCalled := false

		// 定义一个适配器函数,用于在调用下一个中间件时设置标记
		nextAdapter := func(http.ResponseWriter, *http.Request) {
			nextCalled = true
			context.Next()
		}

		// 使用 restgate 实例来验证请求,并传入适配器函数
		gate.ServeHTTP(context.Writer, context.Request, nextAdapter)

		// 如果没有调用下一个中间件,则返回未经授权的状态码
		if !nextCalled {
			context.AbortWithStatus(http.StatusUnauthorized)
		}
	}
}
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值