Golang中Gin框架为何不应接受来自其他工具的请求

Golang中Gin框架为何不应接受来自其他工具的请求 我有一个使用 Gin 框架编写的 REST API。

我希望对其进行限制。它应该只允许来自应用程序的请求,在生产环境中不应接受来自 Postman 等工具的请求。

7 回复

API应仅从Angular应用程序调用,而不能通过Postman或任何其他HTTP客户端调用。

更多关于Golang中Gin框架为何不应接受来自其他工具的请求的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


听起来你可能想为你的API实现某种形式的身份验证,并希望你的客户端应用在发起API请求之前先进行身份验证。

首先,我想了解你提出这个要求的“原因”。

但一般来说,你无法限制谁使用你的API;理论上,你可以在某些特殊的头部(或Cookie)中放入一些信息,但没有人能阻止使用Postman的人设置相同的头部。

之所以要问“原因”,是因为我们需要理解背后的理由,或许能提供替代的解决方案。

packs:

Api should work from angular application only

正如之前指出的,我发现唯一的方法是通过限制仅允许本地主机或内部IP访问。

在我的场景中,我有三个VPS。一个用于应用程序,一个用于API,最后一个用于数据库。API和数据库被锁定(“安全隔离”),禁止直接进行互联网通信。所有与API的通信都通过Web应用程序完成,且仅限本地主机或内部IP地址。

这是一种物理层面的解决方案,由VPS防火墙保护,因此无法通过常规方式从互联网访问。

正如前面的人所说,你应该在你的路由上实现认证。例如这里:

r := gin.Default();

r.POST("/create-something", auth.VerifyJWT, packageCreate.CreateSomething)

VerifyJWT 是我用于检查 JWT 令牌的自定义函数。 正如你在这里看到的我的做法,我首先验证用户是否已登录(我在 cookies 中使用 JWT 令牌)。所以它首先检查令牌是否有效(即用户是否已登录)。如果有效,它将继续执行 “package.Create…” 来触发所需的函数。

如果在 VerifyJWT 步骤失败,它将不允许用户创建帖子。即使有人获取了你的 API 并尝试通过 postman(未登录状态)创建帖子,它也会返回一个错误,提示“用户未登录”。

我想这就是你要找的。

是的。听起来你需要退一步,先学习一下身份验证的相关知识。也许可以从这里开始?

Postman API Platform

什么是 API 身份验证?优势、方法与最佳实践 | Postman

了解 API 身份验证如何帮助团队保护敏感数据、与用户建立信任并维护公司声誉。

或者这里:

Google Cloud Blog

实现 REST API 身份验证的 5 种方法 | Google Cloud Blog

API 已成为主要的攻击媒介,也是开发人员和安全专业人员共同关注的焦点。为你的 REST API 实施强大的身份验证机制至关重要,这是保护传输中数据最简单的方法…

但关键点是:如果没有某种私钥,你就无法限制对 API 的访问(例如,这就是你如何与 Stripe 等 API 进行身份验证;因为你在自己控制的安全环境中保存了一个密钥)。而且你不能将私钥打包到你的客户端应用程序中(这是一个你无法控制的环境)。因此,你需要一种面向公众的方式,让客户端能够验证自己的身份。

在 Gin 框架中限制请求来源,可以通过以下方式实现:

  1. 使用中间件验证请求头:检查特定自定义请求头,例如 X-Requested-With 或应用专属令牌
  2. 验证 User-Agent:检查请求头中的 User-Agent 是否来自你的应用
  3. 使用 API 密钥认证:要求所有请求必须包含有效的 API 密钥

以下是具体实现示例:

方法1:自定义请求头验证中间件

package main

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

func AppOnlyMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 检查自定义请求头
        appToken := c.GetHeader("X-App-Token")
        if appToken != "your-secret-token-here" {
            c.JSON(http.StatusForbidden, gin.H{
                "error": "Access denied",
            })
            c.Abort()
            return
        }
        c.Next()
    }
}

func main() {
    r := gin.Default()
    
    // 应用中间件到所有路由
    r.Use(AppOnlyMiddleware())
    
    r.GET("/api/data", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"message": "Authorized access"})
    })
    
    r.Run(":8080")
}

方法2:User-Agent 验证

func ValidateUserAgent() gin.HandlerFunc {
    return func(c *gin.Context) {
        userAgent := c.GetHeader("User-Agent")
        
        // 只允许特定应用标识的请求
        allowedAgents := []string{
            "MyMobileApp/1.0",
            "MyWebApp/2.0",
        }
        
        authorized := false
        for _, agent := range allowedAgents {
            if strings.Contains(userAgent, agent) {
                authorized = true
                break
            }
        }
        
        if !authorized {
            c.JSON(http.StatusForbidden, gin.H{
                "error": "Invalid client application",
            })
            c.Abort()
            return
        }
        
        c.Next()
    }
}

方法3:API 密钥认证(推荐用于生产环境)

func APIKeyAuth(validKeys map[string]bool) gin.HandlerFunc {
    return func(c *gin.Context) {
        apiKey := c.GetHeader("X-API-Key")
        
        if apiKey == "" {
            apiKey = c.Query("api_key")
        }
        
        if !validKeys[apiKey] {
            c.JSON(http.StatusUnauthorized, gin.H{
                "error": "Invalid or missing API key",
            })
            c.Abort()
            return
        }
        
        c.Next()
    }
}

func main() {
    r := gin.Default()
    
    // 预定义有效的 API 密钥
    validAPIKeys := map[string]bool{
        "prod-key-abc123": true,
        "prod-key-def456": true,
    }
    
    r.Use(APIKeyAuth(validAPIKeys))
    
    r.GET("/api/protected", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"data": "Protected resource"})
    })
    
    r.Run(":8080")
}

方法4:结合多种验证方式

func ComprehensiveAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 1. 验证 API 密钥
        apiKey := c.GetHeader("X-API-Key")
        if apiKey != "production-secret-key" {
            c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid API key"})
            c.Abort()
            return
        }
        
        // 2. 验证请求来源(可选)
        if c.GetHeader("X-Request-Source") != "mobile-app" {
            c.JSON(http.StatusForbidden, gin.H{"error": "Invalid request source"})
            c.Abort()
            return
        }
        
        // 3. 验证时间戳防重放攻击
        timestamp := c.GetHeader("X-Timestamp")
        if !isValidTimestamp(timestamp) {
            c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid timestamp"})
            c.Abort()
            return
        }
        
        c.Next()
    }
}

func isValidTimestamp(ts string) bool {
    // 实现时间戳验证逻辑
    return true
}

重要注意事项:

  1. API 密钥应通过环境变量配置,不要硬编码在代码中
  2. 生产环境应使用 HTTPS 防止密钥被截获
  3. 考虑实现速率限制 防止滥用
  4. 定期轮换 API 密钥 增强安全性

这些方法可以有效限制只有你的应用程序能够访问 API,阻止来自 Postman 等工具的未授权请求。

回到顶部