casbin访问控制框架

Casbin是一个强大的,高效的开源访问控制框架,其权限管理机制支持多种访问控制模型,各个编程语言都对casbin有支持

目前我的所有项目,都是基于简单的角色管理来的

分为了三类,未登录能访问的,普通用户能访问的,管理员能访问的

后续做rbac系统,那就会把角色和访问控制结合起来,casbin就可以更好的帮助我去做这个事情

网上关于casbin的文档其实还挺多的,但是他们的案例很多都不太适合,新手第一次看很容易看懵

这个文档就结合实际的web中的访问控制来进行讲解

基本使用

下载

go get github.com/casbin/casbin/v2

casbin有两个部分组成,一个是配置文件,一个是规则集

初次学习,先把demo跑通,先不用搞懂这两个东西是什么

准备两个文件,model.pml和policy.csv文件

model.pml

[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

[policy_effect]
e = some(where (p.eft == allow))

policy.csv

p, zhangsan, /index, GET
p, zhangsan, /home, GET
p, zhangsan, /users, GET
p, zhangsan, /users, POST
p, wangwu, /index, GET
p, wangwu, /home, GET

demo.go

package main

import (
  "fmt"
  "github.com/casbin/casbin/v2"
  "log"
)

func check(e *casbin.Enforcer, sub, obj, act string) {
  ok, _ := e.Enforce(sub, obj, act)
  if ok {
    fmt.Printf("%s CAN %s %s\n", sub, act, obj)
  } else {
    fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
  }
}

func main() {
  e, err := casbin.NewEnforcer("./model.pml", "./policy.csv")
  if err != nil {
    log.Fatalf("NewEnforecer failed:%v\n", err)
  }

  check(e, "zhangsan", "/index", "GET")
  check(e, "zhangsan", "/home", "GET")
  check(e, "zhangsan", "/users", "POST")
  check(e, "wangwu", "/users", "POST")
}

正常情况下,你会得到

zhangsan CAN GET /index
zhangsan CAN GET /home
zhangsan CAN POST /users
wangwu CANNOT POST /users

Casbin配置文件

最开始看到model.pml和policy.csv文件,估计还是一脸懵的

那让我们来看看casbin的配置文件

Request

代表请求。看我们上面的例子:

[request_definition]
r = sub, obj, act

代表一个请求有三个标准元素,请求主体,请求对象,请求操作

以用户 fengfeng 用GET请求 /api/users接口为例:

请求主体就是 fengfeng

请求对象就是 /api/users

请求操作就是 GET

Policy和Policy_Rule

Policy 代表策略,它表示具体的权限定义的规则是什么

在policy.csv文件中定义的策略就是policy_rule。它和Policy是一一对应的。

例如我们上面的例子:

[policy_definition]
p = sub, obj, act

我们定义了 policy 的规则如此,那么我们在 policy.csv 中每⼀⾏定义的 policy_rule 就必须和这个属性⼀⼀对应

Matcher

有请求,有规则,那么请求是否匹配某个规则,则是matcher进行判断的,例如:

[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

表示当

r.sub == p.sub && r.obj == p.obj && r.act == p.act

的时候返回true,否则返回false,也就是三者完全匹配的时候,返回true

Effect

Effect ⽤来判断如果⼀个请求满⾜了规则,是否需要同意请求。它的规则⽐较复杂⼀些。

比如我们这里:

[policy_effect]
e = some(where (p.eft == allow))

这里的some表示括号中的表达式个数大于等于1就行。 我们这句话的意思就是将request和所有policy比对完之后,所有policy的策略结果(p.eft)为allow的个数>=1,整个请求的策略就是true

访问控制模型

除了文档中提到的,casbin还支持多种访问控制模型

Supported Models | Casbin

ACL访问控制

Access Control List (访问控制列表)

ACL是最早也是最基本的一种访问控制机制,它的原理非常简单:

每一项资源,都配有一个列表,这个列表记录的就是哪些用户可以对这项资源执行CRUD中的那些操作。当系统试图访问这项资源时,会首先检查这个列表中是否有关于当前用户的访问权限,从而确定当前用户可否执行相应的操作。总得来说,ACL是一种面向资源的访问控制模型,它的机制是围绕 资源 展开的。

上面的demo就是acl访问控制的例子

增加规则

e.AddPolicy("wangwu", "/users", "POST")
e.SavePolicy()

删除规则

e.RemovePolicy("wangwu", "/users", "POST")
e.SavePolicy()

RBAC访问控制

ACL模型在用户和资源都比较少的情况下没什么问题,但是用户和资源量一大,ACL就会变得异常繁琐

想象一下,每次新增一个用户,都要把他需要的权限重新设置一遍是多么地痛苦

RBAC(role-based-access-control)模型通过引入角色(role)这个中间层来解决这个问题

每个用户都属于一个角色,例如开发者、管理员、运维等,每个角色都有其特定的权限,权限的增加和删除都通过角色来进行

这样新增一个用户时,我们只需要给他指派一个角色,他就能拥有该角色的所有权限

修改角色的权限时,属于这个角色的用户权限就会相应的修改

mode.pml文件

[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

policy.csv

p, admin, /index, GET
p, admin, /home, GET
p, admin, /users, GET
p, admin, /users, POST
p, yunwei, /index, GET
p, yunwei, /home, GET
g, zhangsan, admin
g, wangwu, yunwei

判断的代码还是一样的

check(e, "zhangsan", "/index", "GET")
check(e, "zhangsan", "/home", "GET")
check(e, "zhangsan", "/users", "POST")
check(e, "wangwu", "/users", "POST")

但是有了角色之后,系统新增一个用户,只需要给这个用户分配角色

然后可以在角色管理中新增一个角色,然后设置角色的访问权限,可以访问哪些接口

在这个模式下,一个用户是可以有多个角色的,例如

p, admin, /index, GET
p, admin, /home, GET
p, admin, /users, GET
p, admin, /users, POST
p, yunwei, /index, GET
p, yunwei, /home, GET
g, zhangsan, admin
g, wangwu, yunwei
g, wangwu, admin

增加角色

e.AddPolicy("kuaiji", "/users", "GET")
e.SavePolicy()

增加用户-角色对应关系

e.AddRoleForUser("zhangsan", "kuaiji")
e.SavePolicy()

删除用户-角色对应关系

e.RemoveGroupingPolicy("zhangsan", "kuaiji")
e.SavePolicy()

删除角色

e.RemovePolicy("kuaiji", "/users", "GET")
e.SavePolicy()

gorm接入Casbin

其实基于上面的rbac,就可以做一个角色访问控制功能了

但是目前整个规则集是存储在文件中的

实际应用中肯定还是会把casbin的规则集放到数据库里面

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

init gorm

package core

import (
  "fmt"
  "github.com/sirupsen/logrus"
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
  "gorm.io/gorm/logger"
  "time"
)

func InitGorm() *gorm.DB {
  dsn := "root:root@tcp(127.0.0.1:3306)/rule_db?charset=utf8mb4&parseTime=True&loc=Local"

  var mysqlLogger logger.Interface
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
    Logger:                                   mysqlLogger,
    DisableForeignKeyConstraintWhenMigrating: true,
  })
  if err != nil {
    logrus.Error(fmt.Sprintf("[%s] mysql连接失败", dsn))
    panic(err)
  }
  sqlDB, _ := db.DB()
  sqlDB.SetMaxIdleConns(10)               // 最大空闲连接数
  sqlDB.SetMaxOpenConns(100)              // 最多可容纳
  sqlDB.SetConnMaxLifetime(time.Hour * 4) // 连接最大复用时间,不能超过mysql的wait_timeout
  return db
}


迁移表的时候,把CasbinRule一起生成出来

gormadapter "github.com/casbin/gorm-adapter/v3"

global.DB.AutoMigrate(
  &gormadapter.CasbinRule{},
)

casbin连接关联gorm

global.DB = core.InitGorm()
a, _ := gormadapter.NewAdapterByDB(global.DB)
m, err := model.NewModelFromFile("./testdata/model.pml")
if err != nil {
  logrus.Error("字符串加载模型失败!", err)
  return
}
e, _ := casbin.NewCachedEnforcer(m, a)
e.SetExpireTime(60 * 60)
_ = e.LoadPolicy()

rbac的增删改查

e.AddPolicy("admin", "/api/users", "GET")
e.AddRoleForUser("zhangsan", "admin")
check(e, "zhangsan", "/api/users", "GET")
e.RemoveGroupingPolicy("zhangsan", "admin")
e.RemovePolicy("admin", "/api/users", "GET")
check(e, "zhangsan", "/api/users", "GET")
e.SavePolicy()
check(e, "zhangsan", "/api/users", "GET")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值