0


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

官方文档地址(中文):https://gin-gonic.com/zh-cn/docs/
注:本教程采用工作区机制,所以一个项目下载了Gin框架,其余项目就无需重复下载,想了解的读者可阅读第一节:Gin操作指南:开山篇。
本节演示日志与安全相关的API,包括定义路由日志的格式;如何记录日志;安全页眉;使用BasicAuth中间件;使用HTTP方法。其中控制日志输出颜色就是将

gin.DisableConsoleColor()

替换为

gin.ForceConsoleColor()

,读者可自行尝试,本文不做演示。在开始之前,我们需要在”04日志与安全“目录下打开命令行,执行如下命令来创建子目录:

mkdir 定义路由日志的格式 如何记录日志 安全页眉 使用BasicAuth中间件 使用HTTP方法

目录

一、定义路由日志的格式

默认的路由日志格式:

[GIN-debug] POST   /foo                      --> main.main.func1 (3 handlers)[GIN-debug] GET    /bar                      --> main.main.func2 (3 handlers)[GIN-debug] GET    /status                   --> main.main.func3 (3 handlers)

如果你想要以指定的格式(例如 JSON,key values 或其他格式)记录信息,则可以使用

gin.DebugPrintRouteFunc

指定格式。 在下面的示例中,我们使用标准日志包记录所有路由,但你可以使用其他满足你需求的日志工具。

package main

import("log"// 导入标准日志包,用于记录日志信息"net/http"// 导入 net/http 包,用于 HTTP 状态码和相关类型"github.com/gin-gonic/gin"// 导入 Gin 框架)funcmain(){
    r := gin.Default()// 创建一个默认的 Gin 路由引擎// 自定义路由打印函数,以指定的格式记录路由信息
    gin.DebugPrintRouteFunc =func(httpMethod, absolutePath, handlerName string, nuHandlers int){// 使用标准日志包打印每个路由的 HTTP 方法、绝对路径、处理函数名称和处理函数数量
        log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers)}// 定义 POST 路由 /foo
    r.POST("/foo",func(c *gin.Context){// 返回 JSON 响应,内容为 "foo" 和 HTTP 状态码 200
        c.JSON(http.StatusOK,"foo")})// 定义 GET 路由 /bar
    r.GET("/bar",func(c *gin.Context){// 返回 JSON 响应,内容为 "bar" 和 HTTP 状态码 200
        c.JSON(http.StatusOK,"bar")})// 定义 GET 路由 /status
    r.GET("/status",func(c *gin.Context){// 返回 JSON 响应,内容为 "ok" 和 HTTP 状态码 200
        c.JSON(http.StatusOK,"ok")})// 监听并在 0.0.0.0:8080 上启动服务
    r.Run()// 启动 Gin 路由引擎}

效果
GET测试
在这里插入图片描述

POST测试
在这里插入图片描述

二、如何记录日志

package main

import("io"// 导入 io 包,用于多路写入"os"// 导入 os 包,用于文件操作"github.com/gin-gonic/gin"// 导入 Gin 框架)funcmain(){// 禁用控制台颜色,将日志写入文件时不需要控制台颜色。
    gin.DisableConsoleColor()// 创建一个文件用于保存日志,文件名为 "gin.log"
    f, err := os.Create("gin.log")if err !=nil{panic(err)// 如果文件创建失败,抛出错误}// 设置 Gin 的默认写入器,将日志写入到刚创建的文件中
    gin.DefaultWriter = io.MultiWriter(f)// 如果需要同时将日志写入文件和控制台,请使用以下代码。// gin.DefaultWriter = io.MultiWriter(f, os.Stdout)// 创建默认的 Gin 路由引擎
    router := gin.Default()// 定义 GET 路由 /ping
    router.GET("/ping",func(c *gin.Context){// 返回字符串 "pong" 和 HTTP 状态码 200
        c.String(200,"pong")})// 监听并在 0.0.0.0:8080 上启动服务
    router.Run(":8080")// 启动 Gin 路由引擎}

效果
注意,启动后控制台是没有输出的,因为屏蔽了颜色,但go.mod旁边会出现gin.log文件,打开即可看到控制台的输出
在这里插入图片描述
打开浏览器,测试,log文件会变化
在这里插入图片描述

三、安全页眉

使用安全标头保护网络应用程序免受常见安全漏洞的攻击非常重要。本示例将向您展示如何在 Gin 应用程序中添加安全标头,以及如何避免与主机标头注入相关的攻击(SSRF、开放重定向)。

package main

import("net/http"// 导入 net/http 包,用于 HTTP 相关的功能"github.com/gin-gonic/gin"// 导入 Gin 框架)funcmain(){// 创建一个默认的 Gin 路由引擎
    r := gin.Default()// 设置期望的主机头部(Host Header)
    expectedHost :="localhost:8080"// 设置安全标头的中间件
    r.Use(func(c *gin.Context){// 检查请求中的 Host 是否符合预期if c.Request.Host != expectedHost {// 如果 Host 不正确,返回 400 错误并结束请求
            c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error":"Invalid host header"})return}// 设置安全标头以保护应用程序
        c.Header("X-Frame-Options","DENY")// 防止点击劫持
        c.Header("Content-Security-Policy","default-src 'self'; connect-src *; font-src *; script-src-elem * 'unsafe-inline'; img-src * data:; style-src * 'unsafe-inline';")// 防止跨站脚本攻击
        c.Header("X-XSS-Protection","1; mode=block")// 启用 XSS 保护
        c.Header("Strict-Transport-Security","max-age=31536000; includeSubDomains; preload")// 强制使用 HTTPS
        c.Header("Referrer-Policy","strict-origin")// 控制引用来源
        c.Header("X-Content-Type-Options","nosniff")// 防止 MIME 类型嗅探
        c.Header("Permissions-Policy","geolocation=(),midi=(),sync-xhr=(),microphone=(),camera=(),magnetometer=(),gyroscope=(),fullscreen=(self),payment=()")// 控制特定功能的权限
        c.Next()// 继续处理请求})// 定义 GET 路由 /ping
    r.GET("/ping",func(c *gin.Context){// 返回 JSON 响应,包含 "pong" 消息
        c.JSON(200, gin.H{"message":"pong",// 消息内容})})// 启动服务,监听 0.0.0.0:8080
    r.Run()// listen and serve on 0.0.0.0:8080}

打开postman,输入

http://localhost:8080/ping

,此时直接点send,或者随意设置headers中的key和value,只要key不是host(不区分大小写),均能正常输出。但如果在headers中设置了key为host(不区分大小写),那么value就必须是代码中设置好的值,这里是

localhost:8080

,否则出错,如图:
在这里插入图片描述

四、使用BasicAuth中间件

package main

import("net/http""github.com/gin-gonic/gin")// 模拟一些私人数据,使用 map 结构存储用户的私人信息var secrets = gin.H{"foo":    gin.H{"email":"[email protected]","phone":"123433"},// 用户 foo 的私人信息"austin": gin.H{"email":"[email protected]","phone":"666"},// 用户 austin 的私人信息"lena":   gin.H{"email":"[email protected]","phone":"523443"},// 用户 lena 的私人信息}funcmain(){
    r := gin.Default()// 创建一个默认的 Gin 路由器// 路由组使用 gin.BasicAuth() 中间件,保护 /admin 路径// gin.Accounts 是 map[string]string 的一种快捷方式,设置用户和密码
    authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{"foo":"bar",// 用户 foo 的密码"austin":"1234",// 用户 austin 的密码"lena":"hello2",// 用户 lena 的密码"manu":"4321",// 用户 manu 的密码}))// /admin/secrets 端点处理,只有经过 Basic Auth 验证的用户可以访问// 当用户访问 "localhost:8080/admin/secrets" 时,将触发此处理函数
    authorized.GET("/secrets",func(c *gin.Context){// 获取当前用户的信息,它是由 BasicAuth 中间件设置的
        user := c.MustGet(gin.AuthUserKey).(string)if secret, ok := secrets[user]; ok {// 如果找到了用户的私人信息,返回该信息
            c.JSON(http.StatusOK, gin.H{"user": user,"secret": secret})}else{// 如果未找到用户的私人信息,返回默认信息
            c.JSON(http.StatusOK, gin.H{"user": user,"secret":"NO SECRET :("})}})// 监听并在 0.0.0.0:8080 上启动服务,等待请求
    r.Run(":8080")}

效果
在这里插入图片描述

五、使用HTTP方法

package main

import("net/http""github.com/gin-gonic/gin")// 获取请求的处理函数funcgetting(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{"message":"GET request successful"})}// 处理 POST 请求的处理函数funcposting(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{"message":"POST request successful"})}// 处理 PUT 请求的处理函数funcputting(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{"message":"PUT request successful"})}// 处理 DELETE 请求的处理函数funcdeleting(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{"message":"DELETE request successful"})}// 处理 PATCH 请求的处理函数funcpatching(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{"message":"PATCH request successful"})}// 处理 HEAD 请求的处理函数funchead(c *gin.Context){// HEAD 请求只返回状态和头部,不返回请求体
    c.Status(http.StatusOK)// 返回状态码 200}// 处理 OPTIONS 请求的处理函数funcoptions(c *gin.Context){// OPTIONS 请求返回允许的 HTTP 方法
    c.JSON(http.StatusOK, gin.H{"methods":"GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS"})}funcmain(){// 禁用控制台颜色// gin.DisableConsoleColor()// 使用默认中间件(logger 和 recovery 中间件)创建 gin 路由
    router := gin.Default()// 定义各个 HTTP 方法的路由及其处理函数
    router.GET("/someGet", getting)// 处理 GET 请求
    router.POST("/somePost", posting)// 处理 POST 请求
    router.PUT("/somePut", putting)// 处理 PUT 请求
    router.DELETE("/someDelete", deleting)// 处理 DELETE 请求
    router.PATCH("/somePatch", patching)// 处理 PATCH 请求
    router.HEAD("/someHead", head)// 处理 HEAD 请求
    router.OPTIONS("/someOptions", options)// 处理 OPTIONS 请求// 默认在 8080 端口启动服务,除非定义了一个 PORT 的环境变量。
    router.Run()// 启动服务并监听端口// router.Run(":3000") // 如果需要硬编码端口号,可以取消注释这一行}

效果
使用 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。
这里只展示一个:
在这里插入图片描述

标签: gin golang

本文转载自: https://blog.csdn.net/weixin_54259326/article/details/142969386
版权归原作者 技术卷 所有, 如有侵权,请联系我们删除。

“Gin框架操作指南08:日志与安全”的评论:

还没有评论