全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-18 12 分钟 ✍️ juanwangdev

Gin RBAC权限模型实现

RBAC 是最常用的权限控制模型,通过用户-角色-权限三层结构实现灵活的访问控制。

RBAC 模型概述

RBAC(Role-Based Access Control)核心要素:

  • 用户(User):系统使用者
  • 角色(Role):权限集合的抽象
  • 权限(Permission):对资源的操作许可
Go
用户 ──关联──> 角色 ──关联──> 权限 ──对应──> 资源操作

数据库设计

表结构

Go
// 用户表
type User struct {
    ID       uint   `gorm:"primaryKey"`
    Username string `gorm:"uniqueIndex;size:50"`
    Password string `gorm:"size:255"`
    Roles    []Role `gorm:"many2many:user_roles"`
}

// 角色表
type Role struct {
    ID          uint         `gorm:"primaryKey"`
    Name        string       `gorm:"uniqueIndex;size:50"`
    Description string       `gorm:"size:200"`
    Permissions []Permission `gorm:"many2many:role_permissions"`
}

// 权限表
type Permission struct {
    ID          uint   `gorm:"primaryKey"`
    Name        string `gorm:"uniqueIndex;size:100"` // 如: user:create, user:delete
    Description string `gorm:"size:200"`
    Resource    string `gorm:"size:50"`              // 资源名称
    Action      string `gorm:"size:20"`              // 操作类型
}

// 关联表由 GORM 自动创建
// user_roles: user_id, role_id
// role_permissions: role_id, permission_id

数据库迁移

Go
func InitDB() *gorm.DB {
    db, err := gorm.Open(mysql.Open("dsn"), &gorm.Config{})
    if err != nil {
        panic("数据库连接失败")
    }

    // 自动迁移
    db.AutoMigrate(&User{}, &Role{}, &Permission{})

    // 初始化基础角色和权限
    initPermissions(db)
    initRoles(db)

    return db
}

func initPermissions(db *gorm.DB) {
    permissions := []Permission{
        {Name: "user:read", Resource: "user", Action: "read", Description: "查看用户"},
        {Name: "user:create", Resource: "user", Action: "create", Description: "创建用户"},
        {Name: "user:update", Resource: "user", Action: "update", Description: "更新用户"},
        {Name: "user:delete", Resource: "user", Action: "delete", Description: "删除用户"},
        {Name: "article:read", Resource: "article", Action: "read", Description: "查看文章"},
        {Name: "article:create", Resource: "article", Action: "create", Description: "创建文章"},
        {Name: "article:update", Resource: "article", Action: "update", Description: "更新文章"},
        {Name: "article:delete", Resource: "article", Action: "delete", Description: "删除文章"},
    }
    db.Create(&permissions)
}

func initRoles(db *gorm.DB) {
    // 管理员角色
    adminRole := Role{Name: "admin", Description: "管理员"}
    db.Create(&adminRole)
    db.Model(&adminRole).Association("Permissions").Append([]Permission{})

    // 编辑角色
    editorRole := Role{Name: "editor", Description: "编辑"}
    db.Create(&editorRole)
    // 只分配文章相关权限

    // 普通用户角色
    userRole := Role{Name: "user", Description: "普通用户"}
    db.Create(&userRole)
    // 只分配读取权限
}

权限查询实现

Go
// 获取用户所有权限
func GetUserPermissions(db *gorm.DB, userID uint) ([]string, error) {
    var permissions []string

    err := db.Table("permissions").
        Select("permissions.name").
        Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
        Joins("JOIN user_roles ON role_permissions.role_id = user_roles.role_id").
        Where("user_roles.user_id = ?", userID).
        Pluck("permissions.name", &permissions)

    return permissions, err
}

// 检查用户是否有特定权限
func HasPermission(db *gorm.DB, userID uint, permission string) bool {
    var count int64
    db.Table("permissions").
        Joins("JOIN role_permissions ON permissions.id = role_permissions.permission_id").
        Joins("JOIN user_roles ON role_permissions.role_id = user_roles.role_id").
        Where("user_roles.user_id = ? AND permissions.name = ?", userID, permission).
        Count(&count)

    return count > 0
}

// 检查用户角色
func HasRole(db *gorm.DB, userID uint, roleName string) bool {
    var count int64
    db.Table("user_roles").
        Joins("JOIN roles ON user_roles.role_id = roles.id").
        Where("user_roles.user_id = ? AND roles.name = ?", userID, roleName).
        Count(&count)

    return count > 0
}

Gin 中间件实现

权限检查中间件

Go
func RBACMiddleware(db *gorm.DB, permission string) gin.HandlerFunc {
    return func(c *gin.Context) {
        // 从上下文获取用户 ID(需配合 JWT 中间件)
        userID, exists := c.Get("user_id")
        if !exists {
            c.JSON(401, gin.H{"error": "未认证"})
            c.Abort()
            return
        }

        // 检查权限
        if !HasPermission(db, userID.(uint), permission) {
            c.JSON(403, gin.H{"error": "无权限访问"})
            c.Abort()
            return
        }

        c.Next()
    }
}

// 角色检查中间件
func RoleMiddleware(db *gorm.DB, roles ...string) gin.HandlerFunc {
    return func(c *gin.Context) {
        userID, exists := c.Get("user_id")
        if !exists {
            c.JSON(401, gin.H{"error": "未认证"})
            c.Abort()
            return
        }

        hasRole := false
        for _, role := range roles {
            if HasRole(db, userID.(uint), role) {
                hasRole = true
                break
            }
        }

        if !hasRole {
            c.JSON(403, gin.H{"error": "角色权限不足"})
            c.Abort()
            return
        }

        c.Next()
    }
}

路由配置

Go
func SetupRoutes(r *gin.Engine, db *gorm.DB) {
    // 公开路由
    r.POST("/login", LoginHandler)
    r.POST("/register", RegisterHandler)

    // 需认证路由
    api := r.Group("/api")
    api.Use(JWTAuthMiddleware())
    {
        // 用户管理 - 需管理员权限
        users := api.Group("/users")
        users.Use(RoleMiddleware(db, "admin"))
        {
            users.GET("", ListUsers)          // user:read
            users.POST("", CreateUser)        // user:create
            users.PUT("/:id", UpdateUser)     // user:update
            users.DELETE("/:id", DeleteUser)  // user:delete
        }

        // 文章管理 - 不同权限
        articles := api.Group("/articles")
        {
            articles.GET("", RBACMiddleware(db, "article:read"), ListArticles)
            articles.POST("", RBACMiddleware(db, "article:create"), CreateArticle)
            articles.PUT("/:id", RBACMiddleware(db, "article:update"), UpdateArticle)
            articles.DELETE("/:id", RBACMiddleware(db, "article:delete"), DeleteArticle)
        }

        // 个人中心 - 无需额外权限
        api.GET("/profile", ProfileHandler)
    }
}

权限缓存优化

Go
import "sync"

type PermissionCache struct {
    cache map[uint][]string // userID -> permissions
    mu    sync.RWMutex
    ttl   time.Duration
}

func NewPermissionCache(ttl time.Duration) *PermissionCache {
    return &PermissionCache{
        cache: make(map[uint][]string),
        ttl:   ttl,
    }
}

func (pc *PermissionCache) Get(userID uint) ([]string, bool) {
    pc.mu.RLock()
    defer pc.mu.RUnlock()
    permissions, exists := pc.cache[userID]
    return permissions, exists
}

func (pc *PermissionCache) Set(userID uint, permissions []string) {
    pc.mu.Lock()
    defer pc.mu.Unlock()
    pc.cache[userID] = permissions
}

func (pc *PermissionCache) Delete(userID uint) {
    pc.mu.Lock()
    defer pc.mu.Unlock()
    delete(pc.cache, userID)
}

// 带缓存的权限中间件
func CachedRBACMiddleware(db *gorm.DB, cache *PermissionCache, permission string) gin.HandlerFunc {
    return func(c *gin.Context) {
        userID := c.GetUint("user_id")

        // 从缓存获取
        permissions, exists := cache.Get(userID)
        if !exists {
            // 从数据库加载
            permissions, _ = GetUserPermissions(db, userID)
            cache.Set(userID, permissions)
        }

        // 检查权限
        for _, p := range permissions {
            if p == permission {
                c.Next()
                return
            }
        }

        c.JSON(403, gin.H{"error": "无权限"})
        c.Abort()
    }
}

角色分配接口

text
// 给用户分配角色
func AssignRole(db *gorm.DB, userID uint, roleID uint) error {
    user := User{ID: userID}
    role := Role{ID: roleID}

    return db.Model(&user).Association("Roles").Append(&role)
}

// 移除用户角色
func RemoveRole(db *gorm.DB, userID uint, roleID uint) error {
    user := User{ID: userID}
    role := Role{ID: roleID}

    return db.Model(&user).Association("Roles").Delete(&role)
}

// 给角色分配权限
func AssignPermission(db *gorm.DB, roleID uint, permissionIDs []uint) error {
    role := Role{ID: roleID}
    var permissions []Permission
    db.Find(&permissions, permissionIDs)

    return db.Model(&role).Association("Permissions").Append(&permissions)
}

// API 接口
func AssignRoleHandler(c *gin.Context) {
    var req struct {
        UserID uint `json:"user_id" binding:"required"`
        RoleID uint `json:"role_id" binding:"required"`
    }

    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "参数错误"})
        return
    }

    if err := AssignRole(db, req.UserID, req.RoleID); err != nil {
        c.JSON(500, gin.H{"error": "分配角色失败"})
        return
    }

    // 清除缓存
    permissionCache.Delete(req.UserID)

    c.JSON(200, gin.H{"message": "角色分配成功"})
}

权限设计原则

原则说明
最小权限只授予必需权限
权责分离关键操作需多角色确认
权限继承支持角色层级继承
动态调整支持运行时修改权限

注意:权限变更后需清除缓存,保证实时生效。

要点总结

  1. 三层模型:用户-角色-权限,通过关联表建立关系
  2. 数据库设计:使用 GORM many2many 自动管理关联表
  3. 中间件实现:基于权限名或角色名检查访问权限
  4. 性能优化:使用缓存减少数据库查询
  5. 权限命名:采用 resource:action 格式,便于管理
  6. 安全原则:最小权限、权责分离、动态调整

📝 发现内容有误?点击此处直接编辑

← 上一篇 Gin JWT认证与鉴权
下一篇 → Gin SQL注入防范
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库