Golang授权认证库设计

最近在Golang项目中需要实现授权认证功能,想请教大家关于库设计的最佳实践。目前主要考虑这几个方面:

  1. 如何设计一个灵活支持多种认证方式(如JWT、OAuth2、API Key)的库结构?
  2. 中间件如何处理权限粒度控制(比如角色、操作权限)?
  3. 有什么推荐的安全存储秘钥/令牌的方案?
  4. 是否需要内置续期/刷新机制?常见的坑有哪些?
  5. 单元测试和性能优化方面有哪些经验可以分享?

希望有实际项目经验的朋友能分享一下架构思路或推荐好用的开源库参考。

2 回复

推荐使用 casbin 库,基于 RBAC 模型实现权限管理。支持 ACL、ABAC 等多种策略,适配多种存储后端。代码简洁,易于集成到 Gin、Echo 等框架中。

更多关于Golang授权认证库设计的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go语言中设计授权认证库时,通常需要考虑认证(Authentication)和授权(Authorization)两个核心部分。以下是一个基于JWT(JSON Web Token)和RBAC(基于角色的访问控制)的简单设计示例,适用于Web应用或API服务。

核心组件设计

  1. 认证模块
    使用JWT进行用户身份验证,生成和验证Token。

  2. 授权模块
    基于RBAC模型,管理用户角色和权限。

  3. 中间件
    用于HTTP请求的拦截和权限验证。


代码实现

1. 定义模型

package models

type User struct {
    ID       string
    Username string
    Roles    []string
}

type Role struct {
    Name        string
    Permissions []string
}

2. JWT认证模块

package auth

import (
    "time"
    "github.com/golang-jwt/jwt/v4"
)

var jwtKey = []byte("your_secret_key")

type Claims struct {
    Username string   `json:"username"`
    Roles    []string `json:"roles"`
    jwt.RegisteredClaims
}

func GenerateToken(user *models.User) (string, error) {
    expirationTime := time.Now().Add(24 * time.Hour)
    claims := &Claims{
        Username: user.Username,
        Roles:    user.Roles,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(expirationTime),
        },
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(jwtKey)
}

func ValidateToken(tokenStr string) (*Claims, error) {
    claims := &Claims{}
    token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
        return jwtKey, nil
    })
    if err != nil || !token.Valid {
        return nil, err
    }
    return claims, nil
}

3. 授权模块

package auth

type RBAC struct {
    roles map[string]*models.Role
}

func NewRBAC() *RBAC {
    return &RBAC{roles: make(map[string]*models.Role)}
}

func (r *RBAC) AddRole(role *models.Role) {
    r.roles[role.Name] = role
}

func (r *RBAC) HasPermission(roleName, permission string) bool {
    role, exists := r.roles[roleName]
    if !exists {
        return false
    }
    for _, p := range role.Permissions {
        if p == permission {
            return true
        }
    }
    return false
}

4. HTTP中间件

package middleware

import (
    "net/http"
    "your_project/auth"
)

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        claims, err := auth.ValidateToken(token)
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }
        // 将用户信息存入上下文
        ctx := context.WithValue(r.Context(), "user", claims)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

func RBACMiddleware(permission string, rbac *auth.RBAC) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            user, ok := r.Context().Value("user").(*auth.Claims)
            if !ok {
                http.Error(w, "Unauthorized", http.StatusUnauthorized)
                return
            }
            hasPermission := false
            for _, role := range user.Roles {
                if rbac.HasPermission(role, permission) {
                    hasPermission = true
                    break
                }
            }
            if !hasPermission {
                http.Error(w, "Forbidden", http.StatusForbidden)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}

使用示例

func main() {
    rbac := auth.NewRBAC()
    rbac.AddRole(&models.Role{Name: "admin", Permissions: []string{"read", "write"}})
    
    http.Handle("/api/protected", middleware.AuthMiddleware(
        middleware.RBACMiddleware("read", rbac)(
            http.HandlerFunc(protectedHandler),
        ),
    ))
    http.ListenAndServe(":8080", nil)
}

func protectedHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Access granted to protected resource"))
}

设计要点

  • 安全性:使用强密钥和HTTPS传输Token。
  • 扩展性:可通过接口抽象存储层(如数据库)。
  • 灵活性:支持动态角色和权限管理。

此设计适用于中小型项目,可根据需求集成OAuth2、数据库持久化等高级功能。

回到顶部