Go项目中使用Casbin实现RBAC权限管理教程

作者:

文章目录
  • 在构建企业级 Go 管理后台系统时,权限管理是一个核心功能。Casbin 是一个强大的、开源的访问控制库,支持多种访问控制模型(ACL、RBAC、ABAC 等)。本文将详细介绍如何在 Go 项目中使用 Casbin 实现完整的 RBAC(基于角色的访问控制)权限管理系统。 本文基于开源项目 gin-layout 的实际实现,这是一个功能完整的 Go Admin 后台管理系统框架,提供了开箱即用的权限管理解决方案。
  • Casbin 是一个强大的、开源的访问控制库,支持多种访问控制模型: ACL (Access Control List) – 访问控制列表 RBAC (Role-Based Access Control) – 基于角色的访问控制 ABAC (Attribute-Based Access Control) – 基于属性的访问控制 RESTful – RESTful 风格的访问控制 Casbin 的核心思想是将访问控制模型与策略分离,通过配置文件定义访问控制模型,通过适配器(Adapter)存储策略规则。
  • 首先,我们需要安装 Casbin 相关的依赖包: go get github.com/casbin/casbin/v2 go get github.com/casbin/gorm-adapter/v3
  • Casbin 使用模型文件(Model)来定义访问控制规则。在项目中,我们创建了 rbac_model.conf 文件: [request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = (g(r.sub, p.sub) && keyMatch3(r.obj, p.obj) && regexMatch(r.act, p.act))
  • 在项目中,我们封装了一个 Casbin 工具包 internal/pkg/utils/casbin/casbin.go: package casbinx import ( “github.com/casbin/casbin/v2” “github.com/casbin/casbin/v2/model” gormadapter “github.com/casbin/gorm-adapter/v3” “gorm.io/gorm” ) type CasbinEnforcer struct { *casbin.Enforcer errInit error tx *gorm.DB model model.Model } var casbinx = &CasbinEnforcer{} // InitEnforcer 初始化 Casbin Enforcer(仅执行一次) func InitEnforcer() error { once.Do(func() { // 1. 加载模型文件 modelPath, err := getModelPath() if err != nil { casbinx.errInit = err return } m, err := model.NewModelFromFile(modelPath) if err != nil { casbinx.errInit = fmt.Errorf(“加载模型失败: %w”, err) return } casbinx.model = m // 2. 创建 GORM 适配器 db := data.MysqlDB() gormadapter.TurnOffAutoMigrate(db) adapter, err := gormadapter.NewAdapterByDB(db) if err != nil { casbinx.errInit = fmt.Errorf(“创建适配器失败: %w”, err) return } // 3. 创建 Enforcer enforcer, err := casbin.NewEnforcer(m, adapter) if err != nil { casbinx.errInit = fmt.Errorf(“创建 Enforcer 失败: %w”, err) return } // 4. 启用自动保存 enforcer.EnableAutoSave(true) casbinx.Enforcer = enforcer }) return casbinx.errInit } // GetEnforcer 返回已初始化的 Enforcer 实例 func GetEnforcer() *CasbinEnforcer { if casbinx.Enforcer == nil { if err := InitEnforcer(); err != nil { return nil } } return casbinx }
  • 在 Gin 框架中,我们通过中间件来实现权限检查: // internal/middleware/admin_auth.go func AdminAuthHandler() gin.HandlerFunc { return func(c *gin.Context) { // 1. 获取用户信息 uid := c.GetUint(“uid”) if uid == 0 { response.Fail(c, e.NotLogin, “请先登录”) c.Abort() return } adminUser := getUserFromContext(c) if adminUser == nil { response.Fail(c, e.NotLogin, “登录已失效,请重新登录”) c.Abort() return } // 2. 超级管理员跳过权限检查 if !isSuperAdmin(adminUser) { // 3. 检查接口权限 if err := checkPermission(c, adminUser); err != nil { if businessErr, ok := err.(*e.BusinessError); ok { response.Fail(c, businessErr.GetCode(), businessErr.GetMessage()) } else { response.Fail(c, e.ServerErr, “权限验证失败”) } c.Abort() return } } c.Next() } } // checkPermission 检查接口权限 func checkPermission(c *gin.Context, adminUser *model.AdminUser) error { enforcer := casbinx.GetEnforcer() if enforcer.Error() != nil { log.Logger.Error(“权限验证初始化失败”, zap.Error(enforcer.Error())) return e.NewBusinessError(e.ServerErr, “权限验证初始化失败”) } // 构建权限检查的 key userKey := fmt.Sprintf(“%s%s%d”, global.CasbinAdminUserPrefix, global.CasbinSeparator, adminUser.ID) path := c.Request.URL.Path method := c.Request.Method // 检查权限 ok, err := enforcer.Enforce(userKey, path, method) if err != nil { log.Logger.Error(“权限验证失败”, zap.Error(err)) return e.NewBusinessError(e.ServerErr, “权限验证失败”) } // 如果没有权限,检查接口是否需要授权 if !ok { if model.NewApi().CheckoutRouteIsAuth(path, method) { return e.NewBusinessError(e.AuthorizationErr, “暂无接口操作权限”) } } return nil }
  • 在实际应用中,权限更新通常需要与业务数据更新在同一个事务中。项目提供了事务支持: // WithTransaction 执行事务中的操作 func (e *CasbinEnforcer) WithTransaction(fc func(e casbin.IEnforcer) error) (err error) { a, ok := e.GetAdapter().(*gormadapter.Adapter) if !ok { return errors.New(“适配器类型错误”) } if e.tx != nil { if !isInTransaction(e.tx) { return errors.New(“请先通过 GORM 开启事务后传入 SetDB”) } defer func() { // 操作完成后,重置适配器 e.SetAdapter(a.Copy()) e.tx = nil }() // 创建事务适配器 gormadapter.TurnOffAutoMigrate(e.tx) txAdapter, err := gormadapter.NewAdapterByDB(e.tx) if err != nil { return err } e.SetAdapter(txAdapter) } err = fc(e.Enforcer) return } 使用示例: // 在 GORM 事务中使用 Casbin db.Transaction(func(tx *gorm.DB) error { // 1. 设置事务 enforcer := casbinx.GetEnforcer().SetDB(tx) // 2. 更新业务数据 // … 业务逻辑 … // 3. 更新权限策略(在事务中) menuName := fmt.Sprintf(“menu:%d”, menuId) return enforcer.EditPolicyPermissions(menuName, policy) })
  • 当策略更新后,需要重新加载策略到内存中: // 重新加载策略 enforcer := casbinx.GetEnforcer() if err := enforcer.LoadPolicy(); err != nil { log.Logger.Error(“重新加载策略失败”, zap.Error(err)) } 注意:虽然启用了 EnableAutoSave(true),但这只保证策略保存到数据库,不会自动加载到内存。在策略更新后,需要手动调用 LoadPolicy() 重新加载。
  • 项目中定义了统一的权限标识前缀: // internal/global/auth.go const ( CasbinAdminUserPrefix = “adminUser” // 用户前缀 CasbinRolePrefix = “role” // 角色前缀 CasbinMenuPrefix = “menu” // 菜单前缀 CasbinDeptPrefix = “dept” // 部门前缀 CasbinSeparator = “:” // 分隔符 ) 权限标识格式: 用户:adminUser:1(用户 ID 为 1) 角色:role:1(角色 ID 为 1) 菜单:menu:1(菜单 ID 为 1) 部门:dept:1(部门 ID 为 1)
  • 统一权限标识格式:使用统一的前缀和分隔符,便于管理和维护 事务一致性:权限更新与业务数据更新应在同一事务中 策略重新加载:策略更新后及时重新加载,确保权限检查使用最新策略 错误处理:完善的错误处理和日志记录,便于问题排查 性能优化:策略加载到内存后,权限检查速度很快,但策略更新后需要重新加载
  • 本文详细介绍了如何在 Go 项目中使用 Casbin 实现 RBAC 权限管理,包括: ✅ Casbin 模型配置 ✅ Enforcer 初始化 ✅ 权限检查中间件 ✅ 策略管理(权限策略和角色继承) ✅ 事务支持 ✅ 实际应用场景 ✅ 常见问题与解决方案 基于本文的实现,你可以快速构建一个功能完整的权限管理系统。如果你需要完整的项目代码,可以参考开源项目 gin-layout,这是一个功能完整的 Go Admin 后台管理系统框架,提供了开箱即用的权限管理解决方案。
  • Casbin 官方文档 gin-layout 项目地址 go-admin-ui 前端项目 API 文档 到此这篇关于Go项目中使用Casbin实现RBAC权限管理教程的文章就介绍到这了,更多相关Go RBAC 权限管理内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客! 您可能感兴趣的文章: Django如何实现RBAC权限管理 Django RBAC权限管理设计过程详解
  • 目录
    • 📖 前言
    • 🎯 什么是 Casbin?
    • 📦 项目依赖
    • 🔧 1. Casbin 模型配置
      • 配置说明
    • 🚀 2. Casbin 初始化
      • 关键点说明
    • 🔐 3. 权限检查中间件
      • 权限检查流程
    • 📊 4. 策略管理
      • 4.1 策略存储格式
      • 4.2 策略类型
        • P 策略(权限策略)
        • G 策略(角色继承)
      • 4.3 编辑权限策略
        • 4.4 编辑角色继承
        • 🏗️ 5. 实际应用场景
          • 5.1 菜单权限管理
            • 5.2 用户角色分配
              • 5.3 角色权限管理
              • 🔄 6. 事务支持
                • 📝 7. 策略重新加载
                  • 🎯 8. 权限标识规范
                    • 🚨 9. 常见问题与解决方案
                      • 9.1 权限更新后不生效
                        • 9.2 事务回滚后策略未恢复
                          • 9.3 路径匹配问题
                          • 📚 10. 最佳实践
                            • 🎉 总结
                              • 🔗 相关资源

                                在构建企业级 Go 管理后台系统时,权限管理是一个核心功能。Casbin 是一个强大的、开源的访问控制库,支持多种访问控制模型(ACL、RBAC、ABAC 等)。本文将详细介绍如何在 Go 项目中使用 Casbin 实现完整的 RBAC(基于角色的访问控制)权限管理系统。

                                本文基于开源项目 gin-layout 的实际实现,这是一个功能完整的 Go Admin 后台管理系统框架,提供了开箱即用的权限管理解决方案。

                                Casbin 是一个强大的、开源的访问控制库,支持多种访问控制模型:

                                • ACL (Access Control List) – 访问控制列表
                                • RBAC (Role-Based Access Control) – 基于角色的访问控制
                                • ABAC (Attribute-Based Access Control) – 基于属性的访问控制
                                • RESTful – RESTful 风格的访问控制

                                Casbin 的核心思想是将访问控制模型与策略分离,通过配置文件定义访问控制模型,通过适配器(Adapter)存储策略规则。

                                首先,我们需要安装 Casbin 相关的依赖包:

                                go get github.com/casbin/casbin/v2
                                go get github.com/casbin/gorm-adapter/v3
                                

                                Casbin 使用模型文件(Model)来定义访问控制规则。在项目中,我们创建了 rbac_model.conf 文件:

                                [request_definition]
                                r = sub, obj, act
                                
                                [policy_definition]
                                p = sub, obj, act
                                
                                [role_definition]
                                g = _, _
                                
                                [policy_effect]
                                e = some(where (p.eft == allow))
                                
                                [matchers]
                                m = (g(r.sub, p.sub) && keyMatch3(r.obj, p.obj) && regexMatch(r.act, p.act))
                                

                                • request_definition: 定义请求格式,r = sub, obj, act 表示请求包含主体(subject)、对象(object)和操作(action)
                                • policy_definition: 定义策略格式,p = sub, obj, act 表示策略包含主体、对象和操作
                                • role_definition: 定义角色继承关系,g = _, _ 表示角色继承关系(如 g, user:1, role:1 表示用户1继承角色1)
                                • policy_effect: 定义策略效果,e = some(where (p.eft == allow)) 表示只要有一个策略允许就允许访问
                                • matchers: 定义匹配规则
                                  • g(r.sub, p.sub): 检查请求主体是否继承策略主体(角色继承)
                                  • keyMatch3(r.obj, p.obj): 使用 keyMatch3 函数匹配路径(支持 * 通配符)
                                  • regexMatch(r.act, p.act): 使用正则表达式匹配 HTTP 方法

                                在项目中,我们封装了一个 Casbin 工具包 internal/pkg/utils/casbin/casbin.go

                                package casbinx
                                
                                import (
                                    "github.com/casbin/casbin/v2"
                                    "github.com/casbin/casbin/v2/model"
                                    gormadapter "github.com/casbin/gorm-adapter/v3"
                                    "gorm.io/gorm"
                                )
                                
                                type CasbinEnforcer struct {
                                    *casbin.Enforcer
                                    errInit error
                                    tx      *gorm.DB
                                    model   model.Model
                                }
                                
                                var casbinx = &CasbinEnforcer{}
                                
                                // InitEnforcer 初始化 Casbin Enforcer(仅执行一次)
                                func InitEnforcer() error {
                                    once.Do(func() {
                                        // 1. 加载模型文件
                                        modelPath, err := getModelPath()
                                        if err != nil {
                                            casbinx.errInit = err
                                            return
                                        }
                                
                                        m, err := model.NewModelFromFile(modelPath)
                                        if err != nil {
                                            casbinx.errInit = fmt.Errorf("加载模型失败: %w", err)
                                            return
                                        }
                                        casbinx.model = m
                                
                                        // 2. 创建 GORM 适配器
                                        db := data.MysqlDB()
                                        gormadapter.TurnOffAutoMigrate(db)
                                        adapter, err := gormadapter.NewAdapterByDB(db)
                                        if err != nil {
                                            casbinx.errInit = fmt.Errorf("创建适配器失败: %w", err)
                                            return
                                        }
                                
                                        // 3. 创建 Enforcer
                                        enforcer, err := casbin.NewEnforcer(m, adapter)
                                        if err != nil {
                                            casbinx.errInit = fmt.Errorf("创建 Enforcer 失败: %w", err)
                                            return
                                        }
                                
                                        // 4. 启用自动保存
                                        enforcer.EnableAutoSave(true)
                                
                                        casbinx.Enforcer = enforcer
                                    })
                                    return casbinx.errInit
                                }
                                
                                // GetEnforcer 返回已初始化的 Enforcer 实例
                                func GetEnforcer() *CasbinEnforcer {
                                    if casbinx.Enforcer == nil {
                                        if err := InitEnforcer(); err != nil {
                                            return nil
                                        }
                                    }
                                    return casbinx
                                }
                                

                                1. 单例模式: 使用 sync.Once 确保 Enforcer 只初始化一次
                                2. GORM 适配器: 使用 gorm-adapter 将策略存储在 MySQL 数据库中
                                3. 自动保存: EnableAutoSave(true) 确保策略变更后自动保存到数据库
                                4. 模型加载: 从配置文件加载访问控制模型

                                在 Gin 框架中,我们通过中间件来实现权限检查:

                                // internal/middleware/admin_auth.go
                                
                                func AdminAuthHandler() gin.HandlerFunc {
                                    return func(c *gin.Context) {
                                        // 1. 获取用户信息
                                        uid := c.GetUint("uid")
                                        if uid == 0 {
                                            response.Fail(c, e.NotLogin, "请先登录")
                                            c.Abort()
                                            return
                                        }
                                
                                        adminUser := getUserFromContext(c)
                                        if adminUser == nil {
                                            response.Fail(c, e.NotLogin, "登录已失效,请重新登录")
                                            c.Abort()
                                            return
                                        }
                                
                                        // 2. 超级管理员跳过权限检查
                                        if !isSuperAdmin(adminUser) {
                                            // 3. 检查接口权限
                                            if err := checkPermission(c, adminUser); err != nil {
                                                if businessErr, ok := err.(*e.BusinessError); ok {
                                                    response.Fail(c, businessErr.GetCode(), businessErr.GetMessage())
                                                } else {
                                                    response.Fail(c, e.ServerErr, "权限验证失败")
                                                }
                                                c.Abort()
                                                return
                                            }
                                        }
                                
                                        c.Next()
                                    }
                                }
                                
                                // checkPermission 检查接口权限
                                func checkPermission(c *gin.Context, adminUser *model.AdminUser) error {
                                    enforcer := casbinx.GetEnforcer()
                                    if enforcer.Error() != nil {
                                        log.Logger.Error("权限验证初始化失败", zap.Error(enforcer.Error()))
                                        return e.NewBusinessError(e.ServerErr, "权限验证初始化失败")
                                    }
                                
                                    // 构建权限检查的 key
                                    userKey := fmt.Sprintf("%s%s%d", global.CasbinAdminUserPrefix, global.CasbinSeparator, adminUser.ID)
                                    path := c.Request.URL.Path
                                    method := c.Request.Method
                                
                                    // 检查权限
                                    ok, err := enforcer.Enforce(userKey, path, method)
                                    if err != nil {
                                        log.Logger.Error("权限验证失败", zap.Error(err))
                                        return e.NewBusinessError(e.ServerErr, "权限验证失败")
                                    }
                                
                                    // 如果没有权限,检查接口是否需要授权
                                    if !ok {
                                        if model.NewApi().CheckoutRouteIsAuth(path, method) {
                                            return e.NewBusinessError(e.AuthorizationErr, "暂无接口操作权限")
                                        }
                                    }
                                
                                    return nil
                                }
                                

                                1. 获取用户信息: 从 JWT Token 中解析用户 ID
                                2. 构建用户标识: 使用 adminUser:1 格式标识用户(1 为用户 ID)
                                3. 调用 Enforce: 使用 enforcer.Enforce(userKey, path, method) 检查权限
                                4. 处理结果: 如果没有权限且接口需要授权,返回权限不足错误

                                在数据库中,策略存储在 casbin_rule 表中,表结构如下:

                                CREATE TABLE `casbin_rule` (
                                  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
                                  `ptype` varchar(100) DEFAULT NULL COMMENT '策略类型:p(策略) 或 g(角色继承)',
                                  `v0` varchar(100) DEFAULT NULL COMMENT '主体(subject)',
                                  `v1` varchar(100) DEFAULT NULL COMMENT '对象(object)',
                                  `v2` varchar(100) DEFAULT NULL COMMENT '操作(action)',
                                  `v3` varchar(100) DEFAULT NULL,
                                  `v4` varchar(100) DEFAULT NULL,
                                  `v5` varchar(100) DEFAULT NULL,
                                  PRIMARY KEY (`id`),
                                  UNIQUE KEY `idx_casbin_rule` (`ptype`, `v0`, `v1`, `v2`, `v3`, `v4`, `v5`)
                                ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
                                

                                格式:[p, subject, object, action]

                                示例:

                                • [p, menu:1, /api/v1/user/list, GET] – 菜单1可以访问 /api/v1/user/list 的 GET 方法
                                • [p, role:1, /api/v1/user/*, *] – 角色1可以访问所有 /api/v1/user/* 路径的所有方法

                                格式:[g, user, role]

                                示例:

                                • [g, adminUser:1, role:1] – 用户1继承角色1的所有权限
                                • [g, role:1, menu:1] – 角色1继承菜单1的所有权限
                                • [g, dept:1, role:1] – 部门1继承角色1的所有权限

                                项目中提供了 EditPolicyPermissions 方法来编辑权限策略:

                                // EditPolicyPermissions 编辑策略权限
                                // 策略格式:[p, 菜单ID, 接口路径, 接口方法]
                                func (e *CasbinEnforcer) EditPolicyPermissions(user string, policy [][]string) error {
                                    return e.WithTransaction(func(enforcer casbin.IEnforcer) error {
                                        // 1. 删除用户的所有权限
                                        _, err := enforcer.DeletePermissionsForUser(user)
                                        if err != nil {
                                            return err
                                        }
                                
                                        if len(policy) == 0 {
                                            return nil
                                        }
                                
                                        // 2. 构建完整的策略规则
                                        var policies [][]string
                                        for _, p := range policy {
                                            if len(p) > 0 {
                                                // 策略格式:[user, route, method]
                                                policies = append(policies, append([]string{user}, p...))
                                            }
                                        }
                                
                                        // 3. 批量添加所有策略
                                        ok, err := enforcer.AddPolicies(policies)
                                        if err != nil {
                                            return err
                                        }
                                
                                        if !ok {
                                            return errors.New("添加权限失败")
                                        }
                                
                                        return nil
                                    })
                                }
                                

                                项目中提供了 EditPolicyRoles 方法来编辑角色继承关系:

                                // EditPolicyRoles 编辑策略角色
                                // 策略格式:[g, user, role]
                                func (e *CasbinEnforcer) EditPolicyRoles(user string, policy []string) error {
                                    return e.WithTransaction(func(enforcer casbin.IEnforcer) error {
                                        // 1. 删除用户的所有角色
                                        _, err := enforcer.DeleteRolesForUser(user)
                                        if err != nil {
                                            return err
                                        }
                                
                                        if len(policy) == 0 {
                                            return nil
                                        }
                                
                                        // 2. 构建完整的角色继承规则
                                        var rules [][]string
                                        for _, role := range policy {
                                            if role != "" {
                                                // 策略格式:[user, role]
                                                rules = append(rules, []string{user, role})
                                            }
                                        }
                                
                                        // 3. 批量添加所有角色继承关系
                                        ok, err := enforcer.AddGroupingPolicies(rules)
                                        if err != nil {
                                            return err
                                        }
                                
                                        if !ok {
                                            return errors.New("添加权限失败")
                                        }
                                
                                        return nil
                                    })
                                }
                                

                                在管理后台系统中,菜单通常与 API 接口关联。当用户编辑菜单权限时,需要更新 Casbin 策略:

                                // internal/service/permission/menu.go
                                
                                // UpdateMenuPermissions 更新菜单权限
                                func (s *MenuService) UpdateMenuPermissions(menu *model.Menu, apiList []uint, tx ...*gorm.DB) error {
                                    // 1. 查询 API 信息
                                    apis := model.List(model.NewApi(), "id IN ?", []any{apiList}, model.ListOptionalParams{
                                        SelectFields: []string{"id", "route", "method"},
                                    })
                                
                                    // 2. 构建策略规则
                                    policy := lo.Map(apis, func(api *model.Api, _ int) []string {
                                        return []string{api.Route, api.Method}
                                    })
                                
                                    // 3. 更新 Casbin 策略
                                    menuName := fmt.Sprintf("%s%s%d", global.CasbinMenuPrefix, global.CasbinSeparator, menu.ID)
                                    enforcer := casbinx.GetEnforcer()
                                    if len(tx) > 0 {
                                        enforcer.SetDB(tx[0])
                                    }
                                    
                                    return enforcer.EditPolicyPermissions(menuName, policy)
                                }
                                

                                当为用户分配角色时,需要更新角色继承关系:

                                // internal/service/permission/admin_user.go
                                
                                // EditUserRoles 编辑用户角色
                                func (s *AdminUserService) EditUserRoles(uid uint, roleIds []uint, tx ...*gorm.DB) error {
                                    // 1. 构建角色标识列表
                                    roleList := lo.Map(roleIds, func(roleId uint, _ int) string {
                                        return fmt.Sprintf("%s%s%d", global.CasbinRolePrefix, global.CasbinSeparator, roleId)
                                    })
                                
                                    // 2. 更新角色继承关系
                                    userName := fmt.Sprintf("%s%s%d", global.CasbinAdminUserPrefix, global.CasbinSeparator, uid)
                                    enforcer := casbinx.GetEnforcer()
                                    if len(tx) > 0 {
                                        enforcer.SetDB(tx[0])
                                    }
                                    
                                    return enforcer.EditPolicyRoles(userName, roleList)
                                }
                                

                                角色可以继承菜单的权限,也可以继承其他角色的权限:

                                // internal/service/permission/role.go
                                
                                // EditRoleMenus 编辑角色菜单
                                func (s *RoleService) EditRoleMenus(roleId uint, menuIds []uint, tx ...*gorm.DB) error {
                                    // 1. 构建菜单标识列表
                                    menuList := lo.Map(menuIds, func(menuId uint, _ int) string {
                                        return fmt.Sprintf("%s%s%d", global.CasbinMenuPrefix, global.CasbinSeparator, menuId)
                                    })
                                
                                    // 2. 更新角色继承关系
                                    roleName := fmt.Sprintf("%s%s%d", global.CasbinRolePrefix, global.CasbinSeparator, roleId)
                                    enforcer := casbinx.GetEnforcer()
                                    if len(tx) > 0 {
                                        enforcer.SetDB(tx[0])
                                    }
                                    
                                    return enforcer.EditPolicyRoles(roleName, menuList)
                                }
                                

                                在实际应用中,权限更新通常需要与业务数据更新在同一个事务中。项目提供了事务支持:

                                // WithTransaction 执行事务中的操作
                                func (e *CasbinEnforcer) WithTransaction(fc func(e casbin.IEnforcer) error) (err error) {
                                    a, ok := e.GetAdapter().(*gormadapter.Adapter)
                                    if !ok {
                                        return errors.New("适配器类型错误")
                                    }
                                
                                    if e.tx != nil {
                                        if !isInTransaction(e.tx) {
                                            return errors.New("请先通过 GORM 开启事务后传入 SetDB")
                                        }
                                
                                        defer func() {
                                            // 操作完成后,重置适配器
                                            e.SetAdapter(a.Copy())
                                            e.tx = nil
                                        }()
                                
                                        // 创建事务适配器
                                        gormadapter.TurnOffAutoMigrate(e.tx)
                                        txAdapter, err := gormadapter.NewAdapterByDB(e.tx)
                                        if err != nil {
                                            return err
                                        }
                                        e.SetAdapter(txAdapter)
                                    }
                                
                                    err = fc(e.Enforcer)
                                    return
                                }
                                

                                使用示例:

                                // 在 GORM 事务中使用 Casbin
                                db.Transaction(func(tx *gorm.DB) error {
                                    // 1. 设置事务
                                    enforcer := casbinx.GetEnforcer().SetDB(tx)
                                
                                    // 2. 更新业务数据
                                    // ... 业务逻辑 ...
                                
                                    // 3. 更新权限策略(在事务中)
                                    menuName := fmt.Sprintf("menu:%d", menuId)
                                    return enforcer.EditPolicyPermissions(menuName, policy)
                                })
                                

                                当策略更新后,需要重新加载策略到内存中:

                                // 重新加载策略
                                enforcer := casbinx.GetEnforcer()
                                if err := enforcer.LoadPolicy(); err != nil {
                                    log.Logger.Error("重新加载策略失败", zap.Error(err))
                                }
                                

                                注意:虽然启用了 EnableAutoSave(true),但这只保证策略保存到数据库,不会自动加载到内存。在策略更新后,需要手动调用 LoadPolicy() 重新加载。

                                项目中定义了统一的权限标识前缀:

                                // internal/global/auth.go
                                
                                const (
                                    CasbinAdminUserPrefix = "adminUser"  // 用户前缀
                                    CasbinRolePrefix     = "role"        // 角色前缀
                                    CasbinMenuPrefix     = "menu"        // 菜单前缀
                                    CasbinDeptPrefix     = "dept"        // 部门前缀
                                    CasbinSeparator      = ":"           // 分隔符
                                )
                                

                                权限标识格式:

                                • 用户:adminUser:1(用户 ID 为 1)
                                • 角色:role:1(角色 ID 为 1)
                                • 菜单:menu:1(菜单 ID 为 1)
                                • 部门:dept:1(部门 ID 为 1)

                                问题:更新策略后,权限检查仍然使用旧策略。

                                解决方案:在策略更新后,调用 LoadPolicy() 重新加载策略:

                                enforcer := casbinx.GetEnforcer()
                                // 更新策略
                                enforcer.EditPolicyPermissions(userName, policy)
                                // 重新加载策略
                                enforcer.LoadPolicy()
                                

                                问题:在事务中更新策略,事务回滚后策略未恢复。

                                解决方案:确保在事务提交或回滚后,重新加载策略:

                                db.Transaction(func(tx *gorm.DB) error {
                                    enforcer := casbinx.GetEnforcer().SetDB(tx)
                                    // 更新策略
                                    return enforcer.EditPolicyPermissions(userName, policy)
                                })
                                
                                // 事务提交或回滚后,重新加载策略
                                enforcer := casbinx.GetEnforcer()
                                enforcer.LoadPolicy()
                                

                                问题:使用 keyMatch3 函数时,路径匹配不符合预期。

                                解决方案

                                • keyMatch3 支持 * 通配符,如 /api/v1/user/* 可以匹配 /api/v1/user/list/api/v1/user/detail
                                • 如果需要更精确的匹配,可以使用 keyMatchregexMatch

                                1. 统一权限标识格式:使用统一的前缀和分隔符,便于管理和维护
                                2. 事务一致性:权限更新与业务数据更新应在同一事务中
                                3. 策略重新加载:策略更新后及时重新加载,确保权限检查使用最新策略
                                4. 错误处理:完善的错误处理和日志记录,便于问题排查
                                5. 性能优化:策略加载到内存后,权限检查速度很快,但策略更新后需要重新加载

                                本文详细介绍了如何在 Go 项目中使用 Casbin 实现 RBAC 权限管理,包括:

                                • ✅ Casbin 模型配置
                                • ✅ Enforcer 初始化
                                • ✅ 权限检查中间件
                                • ✅ 策略管理(权限策略和角色继承)
                                • ✅ 事务支持
                                • ✅ 实际应用场景
                                • ✅ 常见问题与解决方案

                                基于本文的实现,你可以快速构建一个功能完整的权限管理系统。如果你需要完整的项目代码,可以参考开源项目 gin-layout,这是一个功能完整的 Go Admin 后台管理系统框架,提供了开箱即用的权限管理解决方案。

                                • Casbin 官方文档
                                • gin-layout 项目地址
                                • go-admin-ui 前端项目
                                • API 文档

                                到此这篇关于Go项目中使用Casbin实现RBAC权限管理教程的文章就介绍到这了,更多相关Go RBAC 权限管理内容请搜索风君子博客以前的文章或继续浏览下面的相关文章希望大家以后多多支持风君子博客!

                                您可能感兴趣的文章:

                                • Django如何实现RBAC权限管理
                                • Django RBAC权限管理设计过程详解

                                站内搜索