Golang中私有与公有端点的实现与区别

Golang中私有与公有端点的实现与区别 假设我们有两个用于获取产品信息的端点。 在这些产品信息中,包含产品的名称和价格。

第一个端点供已认证用户使用,可以访问产品的名称和价格。

第二个端点是公开的,只能访问产品的名称。

在这种情况下,最佳的模式是什么: 是分别创建私有处理函数和公共处理函数吗?如果是,那么如果我需要为每个用户角色(公开、私有、管理员…)提供不同的访问权限,是否应该创建与用户范围一样多的端点? 还是使用相同的处理函数,并添加一个类似 IsPublic 的参数? 我找不到相关的关键词来阅读这方面的内容。

4 回复

您的授权函数可以返回一个描述允许访问字段的策略,视图生成函数则可以使用该属性列表来构建视图。不过,这会使您的视图函数变得复杂得多。我认为,我可能会在视图函数中根据用户的状态进行分支判断。这样,授权逻辑目前可以保持简单,代价是声明性会差一些。

我还没有研究过 oso,但它可能也提供了一些与此相关的指导。

更多关于Golang中私有与公有端点的实现与区别的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


好的,没问题。

我完全同意在中间件中加入授权和认证。 但是,如果我们有第三个字段,包含所有已售出产品的历史记录,该怎么办?

我想为管理员提供所有已售出产品的历史记录访问权限,而公开只提供最近一个月的历史记录。

我应该为两者使用同一个函数并加上一个布尔参数(如 isPublic),还是应该创建单独的函数? 我的意思是,哪种方式更好?

我不是这方面的专家,但我的第一反应是为所有访问级别创建一个路由,并设置两层服务。外层服务将接收某种身份验证令牌,将其解析为用户ID,然后向另一个HTTP服务发起请求,并在HTTP头部中携带该用户ID。

一旦你有了用户ID,就可以创建一些库函数,这些函数引用一个持久化的用户到其访问级别的映射。每个端点都会查找用户的访问级别,然后根据访问级别生成相应的请求。

关于授权实现相关的关键词,你可以阅读关于不同授权方法的内容,例如RBAC。

在Go中实现私有与公有端点,通常建议使用中间件进行权限控制,而不是创建多个处理函数。以下是一个示例实现:

package main

import (
    "encoding/json"
    "net/http"
)

// 产品结构体
type Product struct {
    Name  string  `json:"name"`
    Price float64 `json:"price,omitempty"` // omitempty会在序列化时根据条件省略
}

// 中间件:检查访问权限
func authMiddleware(next http.HandlerFunc, isPublic bool) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if isPublic {
            // 公开端点:直接调用处理函数
            next(w, r)
            return
        }
        
        // 私有端点:检查认证
        token := r.Header.Get("Authorization")
        if token == "" || !isValidToken(token) {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        
        next(w, r)
    }
}

// 统一的处理函数
func getProductHandler(w http.ResponseWriter, r *http.Request, showPrice bool) {
    product := Product{
        Name:  "示例产品",
        Price: 99.99,
    }
    
    // 根据权限决定是否显示价格
    if !showPrice {
        product.Price = 0 // 设置为零值,omitempty会忽略
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(product)
}

// 包装为公开端点
func publicProductHandler(w http.ResponseWriter, r *http.Request) {
    getProductHandler(w, r, false)
}

// 包装为私有端点
func privateProductHandler(w http.ResponseWriter, r *http.Request) {
    getProductHandler(w, r, true)
}

// 辅助函数:验证token
func isValidToken(token string) bool {
    // 实现实际的token验证逻辑
    return token == "valid-token"
}

func main() {
    // 注册路由,应用中间件
    http.HandleFunc("/api/product/public", 
        authMiddleware(publicProductHandler, true))
    
    http.HandleFunc("/api/product/private", 
        authMiddleware(privateProductHandler, false))
    
    http.ListenAndServe(":8080", nil)
}

对于多角色权限控制,可以使用基于角色的访问控制(RBAC)模式:

// 角色定义
type Role string

const (
    RolePublic   Role = "public"
    RoleUser     Role = "user"
    RoleAdmin    Role = "admin"
)

// 带角色的中间件
func roleBasedMiddleware(allowedRoles []Role) func(http.HandlerFunc) http.HandlerFunc {
    return func(next http.HandlerFunc) http.HandlerFunc {
        return func(w http.ResponseWriter, r *http.Request) {
            userRole := getUserRoleFromRequest(r)
            
            // 检查用户角色是否在允许的列表中
            allowed := false
            for _, role := range allowedRoles {
                if userRole == role {
                    allowed = true
                    break
                }
            }
            
            if !allowed {
                http.Error(w, "Forbidden", http.StatusForbidden)
                return
            }
            
            next(w, r)
        }
    }
}

// 统一的产品处理函数
func productHandler(w http.ResponseWriter, r *http.Request) {
    userRole := getUserRoleFromRequest(r)
    product := Product{
        Name:  "示例产品",
        Price: 99.99,
    }
    
    // 根据角色决定返回的数据
    switch userRole {
    case RolePublic:
        product.Price = 0
    case RoleUser:
        // 显示价格
    case RoleAdmin:
        product.Price = 99.99
        // 管理员可以看到额外字段
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(product)
}

// 路由注册示例
func setupRoutes() {
    // 同一个端点,不同角色访问不同数据
    http.HandleFunc("/api/product", 
        roleBasedMiddleware([]Role{RolePublic, RoleUser, RoleAdmin})(productHandler))
    
    // 或者为不同角色创建不同端点
    http.HandleFunc("/api/product/public",
        roleBasedMiddleware([]Role{RolePublic})(productHandler))
    
    http.HandleFunc("/api/product/user",
        roleBasedMiddleware([]Role{RoleUser, RoleAdmin})(productHandler))
}

这种模式的优点:

  1. 保持代码DRY(Don’t Repeat Yourself)
  2. 权限逻辑集中管理
  3. 易于扩展新的角色
  4. 端点结构清晰

实际项目中,可以考虑使用现有的权限库如casbingo-guardian来简化实现。

回到顶部