Casbin是一个强大的,高效的开源访问控制框架,其权限管理机制支持多种访问控制模型,支持多种编程语言。
基本使用
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, xiaoming, /hello, GET
p, xiaoming, /hello/go, GET
p, xiaoming, /user, GET
p, xiaohong, /users, POST
p, xiaohong, /index, GET
p, xiaohong, /home, GET
casbindemo.go
package main
import (
"fmt"
"github.com/casbin/casbin/v2"
"log"
)
func assert(e *casbin.Enforcer, sub, obj, act string) {
enforce, err := e.Enforce(sub, obj, act)
if err != nil {
fmt.Printf("enforce失败%s\n", err.Error())
}
if enforce {
fmt.Printf("%s ASSERT_SUCCESS %s %s\n", sub, act, obj)
} else {
fmt.Printf("%s ASSERT_FAIL %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)
}
assert(e, "xiaoming", "/hello", "GET")
assert(e, "xiaoming", "/hello/go", "GET")
assert(e, "xiaohong", "/hello", "POST")
assert(e, "xiaohong", "/toto", "POST")
}
输出结果
xiaoming ASSERT_SUCCESS GET /hello
xiaoming ASSERT_SUCCESS GET /hello/go
xiaohong ASSERT_FAIL POST /hello
xiaohong ASSERT_FAIL POST /toto
Casbin配置文件详解
Request
代表请求。看我们上面的例子:
[request_definition]
r = sub, obj, act
代表一个请求有三个标准元素,请求主体,请求对象,请求操作
以用户 xiaohong用GET请求 /api/users接口为例:
请求主体就是 xiaohong
请求对象就是 /api/users
请求操作就是 GET
Policy和Policy_Rule
Policy 代表策略,它表示具体的权限定义的规则是什么
在policy.csv文件中定义的策略就是policy_rule。它和Policy是一一对应的。
例如我们上面的demo:
[policy_definition]
p = sub, obj, act
Matcher
有请求,有规则,那么请求是否匹配某个规则,则是matcher进行判断的,例如:
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
表示当m为true时 为验证通过 三个表达式都通过
Effect
Effect ⽤来判断如果⼀个请求满⾜了规则,是否需要同意请求。它的规则⽐较复杂⼀些。
[policy_effect]
e = some(where (p.eft == allow))
这里的some表示括号中的表达式个数大于等于1就行。 我们这句话的意思就是将request和所有policy比对完之后,所有policy的策略结果(p.eft)为allow的个数>=1,整个请求的策略就是true
访问控制模型
除了文档中提到的,casbin还支持多种访问控制模型
https://casbin.org/zh/docs/supported-models/
ACL访问控制
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, dev, /index, GET
p, dev, /home, GET
g, zhangsan, admin
g, wangwu, dev
有了角色之后,系统新增一个用户,只需要给这个用户分配角色
然后可以在角色管理中新增一个角色,然后设置角色的访问权限,可以访问哪些接口
在这个模式下,一个用户是可以有多个角色的
增加角色
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
InitGorm
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
}
生成casbin表结构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")
assert(e, "zhangsan", "/api/users", "GET")
e.RemoveGroupingPolicy("zhangsan", "admin")
e.RemovePolicy("admin", "/api/users", "GET")
assert(e, "zhangsan", "/api/users", "GET")
e.SavePolicy()
assert(e, "zhangsan", "/api/users", "GET")