casbin+gin使用实践总结

287 阅读2分钟

在gin中间件中配置casbin中间件

  1. 在gin的路由链中使用自定义的casbin中间件
PrivateGroup := Router.Group(global.GVA_CONFIG.System.RouterPrefix)
PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
  1. 接着便是拿到核心的Casbin对象,进行enforce
// 定义对象变量(不得不吐槽go的面向对象)
var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
// CasbinHandler 拦截器
func CasbinHandler() gin.HandlerFunc {
        return func(c *gin.Context) {
                waitUse, _ := utils.GetClaims(c)
                //获取请求的PATH
                path := c.Request.URL.Path
                obj := strings.TrimPrefix(path, global.GVA_CONFIG.System.RouterPrefix)
                // 获取请求方法
                act := c.Request.Method
                // 获取用户的角色
                sub := strconv.Itoa(int(waitUse.AuthorityId))
                e := casbinService.Casbin() // 判断策略中是否存在
                success, _ := e.Enforce(sub, obj, act)
                if !success {
                        response.FailWithDetailed(gin.H{}, "权限不足", c)
                        c.Abort()
                        return
                }
                c.Next()
        }
}

在gin环境中拿到用户的路径,权限,和请求方式

casbin核心配置

casbin创建需要模型和适配器。

  • 模型一般都是基于rbac,此部分为固定

  • 而适配器一般是基于数据库

    • 本文使用gorm适配器

数据库表配置

gorm官方配置


type CasbinRule struct {
    ID    uint   `gorm:"primaryKey;autoIncrement"`
    Ptype string `gorm:"size:100"`
    V0    string `gorm:"size:100"`
    V1    string `gorm:"size:100"`
    V2    string `gorm:"size:100"`
    V3    string `gorm:"size:100"`
    V4    string `gorm:"size:100"`
    V5    string `gorm:"size:100"`
}

基于casbin的官方rbac模型

官网rbac模型

[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) && r.obj == p.obj && r.act == p.act

增加删除权限

e.AddPolicy("data2_admin", "/users/page", "GET")
e.AddRoleForUser("alice", "data2_admin")

这种方式的后果就是数据库表中混杂用户对应的角色和角色对应的权限

casbin_rule表结构如下:

将角色和权限分开

角色自己一张表:

整体流程

  1. 往角色表中创建出角色后
  2. 往权限表中增加角色的权限
  3. 分配用户和角色(这个提前可以查出来放在请求头中)

整体数据效果:

image.png

使用keyMatch2适配更加复杂的url

(1)keyMatch:能够支持使用*匹配进行匹配匹配,eg:/api/projects/

(2)keyMatch2:能够支持*号匹配和/:resource的模式,eg:/api/projects/:id

once.Do(func() {
    a, err := gormadapter.NewAdapterByDB(global.GVA_DB)
    if err != nil {
       zap.L().Error("适配数据库失败请检查casbin表是否为InnoDB引擎!", zap.Error(err))
       return
    }
    text := `
    [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 = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
    `
    m, err := model.NewModelFromString(text)
    if err != nil {
       zap.L().Error("字符串加载模型失败!", zap.Error(err))
       return
    }
    syncedCachedEnforcer, _ = casbin.NewSyncedCachedEnforcer(m, a)
    syncedCachedEnforcer.SetExpireTime(60 * 60)
    _ = syncedCachedEnforcer.LoadPolicy()
})
return syncedCachedEnforcer

增加权限代码

func (casbinService *CasbinService) AddPolicies(db *gorm.DB, rules [][]string) error {
    var casbinRules []gormadapter.CasbinRule
    for i := range rules {
       casbinRules = append(casbinRules, gormadapter.CasbinRule{
          Ptype: "p",
          V0:    rules[i][0],
          V1:    rules[i][1],
          V2:    rules[i][2],
       })
    }
    return db.Create(&casbinRules).Error
}

参考自 gin-vue-admin(github.com/flipped-aur…)