Gin框架操作指南08:日志与安全

打印 上一主题 下一主题

主题 994|帖子 994|积分 2982

官方文档地址(中文):https://gin-gonic.com/zh-cn/docs/
注:本教程采用工作区机制,所以一个项目下载了Gin框架,其余项目就无需重复下载,想相识的读者可阅读第一节:Gin操作指南:开山篇。
本节演示日志与安全相关的API,包罗定义路由日志的格式;怎样记录日志;安全页眉;使用BasicAuth中心件;使用HTTP方法。其中控制日志输出颜色就是将gin.DisableConsoleColor()更换为gin.ForceConsoleColor(),读者可自行尝试,本文不做演示。在开始之前,我们需要在”04日志与安全“目次下打开命令行,执行如下命令来创建子目次:
  1. mkdir 定义路由日志的格式 如何记录日志 安全页眉 使用BasicAuth中间件 使用HTTP方法
复制代码

  
一、定义路由日志的格式

默认的路由日志格式:
  1. [GIN-debug] POST   /foo                      --> main.main.func1 (3 handlers)
  2. [GIN-debug] GET    /bar                      --> main.main.func2 (3 handlers)
  3. [GIN-debug] GET    /status                   --> main.main.func3 (3 handlers)
复制代码
如果你想要以指定的格式(例如 JSON,key values 或其他格式)记录信息,则可以使用 gin.DebugPrintRouteFunc 指定格式。 在下面的示例中,我们使用标准日志包记录全部路由,但你可以使用其他满足你需求的日志工具。
  1. package main
  2. import (
  3.         "log"      // 导入标准日志包,用于记录日志信息
  4.         "net/http" // 导入 net/http 包,用于 HTTP 状态码和相关类型
  5.         "github.com/gin-gonic/gin" // 导入 Gin 框架
  6. )
  7. func main() {
  8.         r := gin.Default() // 创建一个默认的 Gin 路由引擎
  9.         // 自定义路由打印函数,以指定的格式记录路由信息
  10.         gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) {
  11.                 // 使用标准日志包打印每个路由的 HTTP 方法、绝对路径、处理函数名称和处理函数数量
  12.                 log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)
  13.         }
  14.         // 定义 POST 路由 /foo
  15.         r.POST("/foo", func(c *gin.Context) {
  16.                 // 返回 JSON 响应,内容为 "foo" 和 HTTP 状态码 200
  17.                 c.JSON(http.StatusOK, "foo")
  18.         })
  19.         // 定义 GET 路由 /bar
  20.         r.GET("/bar", func(c *gin.Context) {
  21.                 // 返回 JSON 响应,内容为 "bar" 和 HTTP 状态码 200
  22.                 c.JSON(http.StatusOK, "bar")
  23.         })
  24.         // 定义 GET 路由 /status
  25.         r.GET("/status", func(c *gin.Context) {
  26.                 // 返回 JSON 响应,内容为 "ok" 和 HTTP 状态码 200
  27.                 c.JSON(http.StatusOK, "ok")
  28.         })
  29.         // 监听并在 0.0.0.0:8080 上启动服务
  30.         r.Run() // 启动 Gin 路由引擎
  31. }
复制代码
效果
GET测试

POST测试

二、怎样记录日志

  1. package main
  2. import (
  3.         "io" // 导入 io 包,用于多路写入
  4.         "os" // 导入 os 包,用于文件操作
  5.         "github.com/gin-gonic/gin" // 导入 Gin 框架
  6. )
  7. func main() {
  8.         // 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
  9.         gin.DisableConsoleColor()
  10.         // 创建一个文件用于保存日志,文件名为 "gin.log"
  11.         f, err := os.Create("gin.log")
  12.         if err != nil {
  13.                 panic(err) // 如果文件创建失败,抛出错误
  14.         }
  15.         // 设置 Gin 的默认写入器,将日志写入到刚创建的文件中
  16.         gin.DefaultWriter = io.MultiWriter(f)
  17.         // 如果需要同时将日志写入文件和控制台,请使用以下代码。
  18.         // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
  19.         // 创建默认的 Gin 路由引擎
  20.         router := gin.Default()
  21.         // 定义 GET 路由 /ping
  22.         router.GET("/ping", func(c *gin.Context) {
  23.                 // 返回字符串 "pong" 和 HTTP 状态码 200
  24.                 c.String(200, "pong")
  25.         })
  26.         // 监听并在 0.0.0.0:8080 上启动服务
  27.         router.Run(":8080") // 启动 Gin 路由引擎
  28. }
复制代码
效果
注意,启动后控制台是没有输出的,由于屏蔽了颜色,但go.mod旁边会出现gin.log文件,打开即可看到控制台的输出

打开欣赏器,测试,log文件会变革

三、安全页眉

使用安全标头保护网络应用步伐免受常见安全漏洞的攻击非常紧张。本示例将向您展示怎样在 Gin 应用步伐中添加安全标头,以及怎样制止与主机标头注入相关的攻击(SSRF、开放重定向)。
  1. package main
  2. import (
  3.         "net/http" // 导入 net/http 包,用于 HTTP 相关的功能
  4.         "github.com/gin-gonic/gin" // 导入 Gin 框架
  5. )
  6. func main() {
  7.         // 创建一个默认的 Gin 路由引擎
  8.         r := gin.Default()
  9.         // 设置期望的主机头部(Host Header)
  10.         expectedHost := "localhost:8080"
  11.         // 设置安全标头的中间件
  12.         r.Use(func(c *gin.Context) {
  13.                 // 检查请求中的 Host 是否符合预期
  14.                 if c.Request.Host != expectedHost {
  15.                         // 如果 Host 不正确,返回 400 错误并结束请求
  16.                         c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid host header"})
  17.                         return
  18.                 }
  19.                 // 设置安全标头以保护应用程序
  20.                 c.Header("X-Frame-Options", "DENY")                                                                                                                                    // 防止点击劫持
  21.                 c.Header("Content-Security-Policy", "default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';") // 防止跨站脚本攻击
  22.                 c.Header("X-XSS-Protection", "1; mode=block")                                                                                                                          // 启用 XSS 保护
  23.                 c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")                                                                                  // 强制使用 HTTPS
  24.                 c.Header("Referrer-Policy", "strict-origin")                                                                                                                           // 控制引用来源
  25.                 c.Header("X-Content-Type-Options", "nosniff")                                                                                                                          // 防止 MIME 类型嗅探
  26.                 c.Header("Permissions-Policy", "geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()")                 // 控制特定功能的权限
  27.                 c.Next()                                                                                                                                                               // 继续处理请求
  28.         })
  29.         // 定义 GET 路由 /ping
  30.         r.GET("/ping", func(c *gin.Context) {
  31.                 // 返回 JSON 响应,包含 "pong" 消息
  32.                 c.JSON(200, gin.H{
  33.                         "message": "pong", // 消息内容
  34.                 })
  35.         })
  36.         // 启动服务,监听 0.0.0.0:8080
  37.         r.Run() // listen and serve on 0.0.0.0:8080
  38. }
复制代码
打开postman,输入http://localhost:8080/ping,此时直接点send,或者随意设置headers中的key和value,只要key不是host(不区分大小写),均能正常输出。但如果在headers中设置了key为host(不区分大小写),那么value就必须是代码中设置好的值,这里是localhost:8080,否则堕落,如图:

四、使用BasicAuth中心件

  1. package main
  2. import (
  3.         "net/http"
  4.         "github.com/gin-gonic/gin"
  5. )
  6. // 模拟一些私人数据,使用 map 结构存储用户的私人信息
  7. var secrets = gin.H{
  8.         "foo":    gin.H{"email": "foo@bar.com", "phone": "123433"},     // 用户 foo 的私人信息
  9.         "austin": gin.H{"email": "austin@example.com", "phone": "666"}, // 用户 austin 的私人信息
  10.         "lena":   gin.H{"email": "lena@guapa.com", "phone": "523443"},  // 用户 lena 的私人信息
  11. }
  12. func main() {
  13.         r := gin.Default() // 创建一个默认的 Gin 路由器
  14.         // 路由组使用 gin.BasicAuth() 中间件,保护 /admin 路径
  15.         // gin.Accounts 是 map[string]string 的一种快捷方式,设置用户和密码
  16.         authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
  17.                 "foo":    "bar",    // 用户 foo 的密码
  18.                 "austin": "1234",   // 用户 austin 的密码
  19.                 "lena":   "hello2", // 用户 lena 的密码
  20.                 "manu":   "4321",   // 用户 manu 的密码
  21.         }))
  22.         // /admin/secrets 端点处理,只有经过 Basic Auth 验证的用户可以访问
  23.         // 当用户访问 "localhost:8080/admin/secrets" 时,将触发此处理函数
  24.         authorized.GET("/secrets", func(c *gin.Context) {
  25.                 // 获取当前用户的信息,它是由 BasicAuth 中间件设置的
  26.                 user := c.MustGet(gin.AuthUserKey).(string)
  27.                 if secret, ok := secrets[user]; ok {
  28.                         // 如果找到了用户的私人信息,返回该信息
  29.                         c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
  30.                 } else {
  31.                         // 如果未找到用户的私人信息,返回默认信息
  32.                         c.JSON(http.StatusOK, gin.H{"user": user, "secret": "NO SECRET :("})
  33.                 }
  34.         })
  35.         // 监听并在 0.0.0.0:8080 上启动服务,等待请求
  36.         r.Run(":8080")
  37. }
复制代码
效果

五、使用HTTP方法

  1. package main
  2. import (
  3.         "net/http"
  4.         "github.com/gin-gonic/gin"
  5. )
  6. // 获取请求的处理函数
  7. func getting(c *gin.Context) {
  8.         c.JSON(http.StatusOK, gin.H{"message": "GET request successful"})
  9. }
  10. // 处理 POST 请求的处理函数
  11. func posting(c *gin.Context) {
  12.         c.JSON(http.StatusOK, gin.H{"message": "POST request successful"})
  13. }
  14. // 处理 PUT 请求的处理函数
  15. func putting(c *gin.Context) {
  16.         c.JSON(http.StatusOK, gin.H{"message": "PUT request successful"})
  17. }
  18. // 处理 DELETE 请求的处理函数
  19. func deleting(c *gin.Context) {
  20.         c.JSON(http.StatusOK, gin.H{"message": "DELETE request successful"})
  21. }
  22. // 处理 PATCH 请求的处理函数
  23. func patching(c *gin.Context) {
  24.         c.JSON(http.StatusOK, gin.H{"message": "PATCH request successful"})
  25. }
  26. // 处理 HEAD 请求的处理函数
  27. func head(c *gin.Context) {
  28.         // HEAD 请求只返回状态和头部,不返回请求体
  29.         c.Status(http.StatusOK) // 返回状态码 200
  30. }
  31. // 处理 OPTIONS 请求的处理函数
  32. func options(c *gin.Context) {
  33.         // OPTIONS 请求返回允许的 HTTP 方法
  34.         c.JSON(http.StatusOK, gin.H{"methods": "GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS"})
  35. }
  36. func main() {
  37.         // 禁用控制台颜色
  38.         // gin.DisableConsoleColor()
  39.         // 使用默认中间件(logger 和 recovery 中间件)创建 gin 路由
  40.         router := gin.Default()
  41.         // 定义各个 HTTP 方法的路由及其处理函数
  42.         router.GET("/someGet", getting)         // 处理 GET 请求
  43.         router.POST("/somePost", posting)       // 处理 POST 请求
  44.         router.PUT("/somePut", putting)         // 处理 PUT 请求
  45.         router.DELETE("/someDelete", deleting)  // 处理 DELETE 请求
  46.         router.PATCH("/somePatch", patching)    // 处理 PATCH 请求
  47.         router.HEAD("/someHead", head)          // 处理 HEAD 请求
  48.         router.OPTIONS("/someOptions", options) // 处理 OPTIONS 请求
  49.         // 默认在 8080 端口启动服务,除非定义了一个 PORT 的环境变量。
  50.         router.Run() // 启动服务并监听端口
  51.         // router.Run(":3000") // 如果需要硬编码端口号,可以取消注释这一行
  52. }
复制代码
效果
使用 Postman 进行测试:
GET 请求:
选择 GET 方法,输入 URL http://localhost:8080/someGet,点击 Send。
POST 请求:
选择 POST 方法,输入 URL http://localhost:8080/somePost,点击 Send。
PUT 请求:
选择 PUT 方法,输入 URL http://localhost:8080/somePut,点击 Send。
DELETE 请求:
选择 DELETE 方法,输入 URL http://localhost:8080/someDelete,点击 Send。
PATCH 请求:
选择 PATCH 方法,输入 URL http://localhost:8080/somePatch,点击 Send。
HEAD 请求:
选择 HEAD 方法,输入 URL http://localhost:8080/someHead,点击 Send。
OPTIONS 请求:
选择 OPTIONS 方法,输入 URL http://localhost:8080/someOptions,点击 Send。
这里只展示一个:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

络腮胡菲菲

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表