golang学习笔记 casbin授权库学习记录

文章介绍了权限控制的不同模型,包括简单的ACL,基于角色的RBAC及其不同变体,以及属性基础的ABAC。重点讨论了Casbin,一个用于实施这些模型的授权库,详细阐述了其工作原理和如何在Golang中使用,包括与数据库适配器(如GORM)的集成以及自定义匹配函数的实现。
摘要由CSDN通过智能技术生成

权限控制管理模型

ACL

ACL 基于用户的权限管理模型
基于用户的概念就是说直接对用户进行权限分配管理,好处是模型构建简单,只需要给用户授予或者取消对应权限即可。但是相对的,如果用户数量庞大的情况下,这套模型就很不实用。因为需要对每一位用户对应权限进行维护,这导致维护成本太高。
ACL模型表结构很简单,只需要用户user表和权限节点node以及user和node的多对多关系表user_node。同时,只需要维护user_node表中user和node关系即可。
ACL的适用于用户数量较小的管理系统中,例如:2112班有10位同学,张三拥有班长权限、李四拥有学委权限、王五拥有纪委权限,其它同学只拥有普通权限。

RBAC

一、RBAC是什么

1、RBAC模型概述

RBAC模型(Role-Based Access Control:基于角色的访问控制)模型是20世纪90年代研究出来的一种新模型,但其实在20世纪70年代的多用户计算时期,这种思想就已经被提出来,直到20世纪90年代中后期,RBAC才在研究团体中得到一些重视,并先后提出了许多类型的RBAC模型。其中以美国George Mason大学信息安全技术实验室(LIST)提出的RBAC96模型最具有代表,并得到了普遍的公认。

RBAC认为权限授权的过程可以抽象地概括为:Who是否可以对What进行How的访问操作,并对这个逻辑表达式进行判断是否为True的求解过程,也即是将权限问题转换为What、How的问题,Who、What、How构成了访问权限三元组;
2、RBAC的组成

在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限。

RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离(区别于ACL模型),极大地方便了权限的管理

User(用户):每个用户都有唯一的UID识别,并被授予不同的角色
Role(角色):不同角色具有不同的权限
Permission(权限):访问权限
用户-角色映射:用户和角色之间的映射关系
角色-权限映射:角色和权限之间的映射

3、RBAC支持的安全原则

RBAC支持三个著名的安全原则:最小权限原则、责任分离原则和数据抽象原则

最小权限原则:RBAC可以将角色配置成其完成任务所需的最小权限集合
责任分离原则:可以通过调用相互独立互斥的角色来共同完成敏感的任务,例如要求一个计账员和财务管理员共同参与统一过账操作
数据抽象原则:可以通过权限的抽象来体现,例如财务操作用借款、存款等抽象权限,而不是使用典型的读、写、执行权限
4、RBAC的优缺点

(1)优点:

简化了用户和权限的关系
易扩展、易维护
(2)缺点:

RBAC模型没有提供操作顺序的控制机制,这一缺陷使得RBAC模型很难适应哪些对操作次序有严格要求的系统
5、RBAC的3种模型

(1)RBAC0

RBAC0,是最简单、最原始的实现方式,也是其他RBAC模型的基础。

在这里插入图片描述

在该模型中,用户和角色之间可以是多对多的关系,即一个用户在不同场景下是可以有不同的角色,例如:项目经理也可能是组长也可能是架构师。同时每个角色都至少有一个权限。这种模型下,用户和权限被分离独立开来,使得权限的授权认证更加灵活。

(2)RBAC1

基于RBAC0模型,引入了角色间的继承关系,即角色上有了上下级的区别。
在这里插入图片描述

角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承。

这种模型适合于角色之间层次分明,可以给角色分组分层。

(3)RBAC2

RBAC2,基于RBAC0模型的基础上,进行了角色的访问控制。
在这里插入图片描述

RBAC2中的一个基本限制是互斥角色的限制,互斥角色是指各自权限可以互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。

该模型有以下几种约束:

互斥角色 :同一用户只能分配到一组互斥角色集合中至多一个角色,支持责任分离的原则。互斥角色是指各自权限互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。常举的例子:在审计活动中,一个角色不能同时被指派给会计角色和审计员角色。
基数约束 :一个角色被分配的用户数量受限;一个用户可拥有的角色数目受限;同样一个角色对应的访问权限数目也应受限,以控制高级权限在系统中的分配。例如公司的领导人有限的;
先决条件角色 :可以分配角色给用户仅当该用户已经是另一角色的成员;对应的可以分配访问权限给角色,仅当该角色已经拥有另一种访问权限。指要想获得较高的权限,要首先拥有低一级的权限。就像我们生活中,国家主席是从副主席中选举的一样。
运行时互斥 :例如,允许一个用户具有两个角色的成员资格,但在运行中不可同时激活这两个角色。

ABAC

基于用户属性进行访问权限判断,对于ABAC模型的学习,后续会再写一篇博客来进行记录。

Casbin介绍

Casbin概述

Casbin 是一个授权库,在我们希望特定用户访问特定的 对象 或实体的流程中可以使用 主题 访问类型,例如 动作 可以是 读取, 写入, 删除 或开发者设置的任何其他动作。 这是Casbin最广泛的使用,它叫做"标准" 或经典 { subject, object, action } 流程。

Casbin能够处理除标准流量以外的许多复杂的许可使用者。 可以添加 角色 (RBAC), 属性 (ABAC) 等。

Casbin工作原理

在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个配置文件。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中结合RBAC角色和ABAC属性,并共享一组策略规则。

PERM模式由四个基础(策略、效果、请求、匹配)组成,描述了资源与用户之间的关系。

请求

定义请求参数。 基本请求是一个元组对象,至少需要主题(访问实体)、目标(访问资源) 和动作(访问方式)

例如,一个请求可能长这样:

 r={sub,obj,act}

它实际上定义了我们应该提供访问控制匹配功能的参数名称和顺序。

策略

定义访问策略的模式。 事实上,它在策略规则文件中界定了字段的名称和顺序。

例如:

 p={sub, obj, act} 
 或 
 p={sub, obj, act, eft}

注:如果未定义eft (policy result),则策略文件中的结果字段将不会被读取, 和匹配的策略结果将默认被允许。

匹配器

匹配请求和政策的规则。

例如:

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

这个简单和常见的匹配规则意味着如果请求的参数(访问实体,访问资源和访问方式)匹配, 如果可以在策略中找到资源和方法,那么策略结果(p.eft)便会返回。 策略的结果将保存在 p.eft 中。

效果

它可以被理解为一种模型,在这种模型中,对匹配结果再次作出逻辑组合判断。

例如:

 e = some (where (p.eft == allow))

这句话意味着,如果匹配的策略结果有一些是允许的,那么最终结果为真。

让我们看看另一个示例:

e = some (where (p.eft == allow)) && !some(where (p.eft == deny) 

此示例组合的逻辑含义是:如果有符合允许结果的策略且没有符合拒绝结果的策略, 结果是为真。 换言之,当匹配策略均为允许(没有任何否认)是为真(更简单的是,既允许又同时否认,拒绝就具有优先地位)。

———————

更多关于的casbin的使用说明请查看官方文档Casbin

Casbin结合golang

基本使用

首先安装casbin

go get github.com/casbin/casbin/v2

Casbin使用配置文件来设置访问控制模型

它有两个配置文件,model.confpolicy.csv。 其中, model.conf 存储了我们的访问模型, 而 policy.csv 存储的是我们具体的用户权限配置。 Casbin的使用非常精炼。 基本上,我们只需要一种主要的结构:enforcer 当构造这个结构的时候, model.conf 和 policy.csv 将会被加载。

import "github.com/casbin/casbin/v2"

e, err := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv")

接下来开始编写配置文件
policy.csv
在这里插入图片描述
model.conf
在这里插入图片描述
配置文件的编写规则具体还得看官方文档给出的解释。
配置文档编写好后,即可开始测试

func main() {
	e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
	if err != nil {
		fmt.Println(err)
		return
	}
	sub := "ylj"
	obj := "date1"
	act := "read"
	act2 := "write"
	//add, err := e.AddPolicy(sub, obj, act2)
	//if err != nil {
	//	return
	//}
	//fmt.Println(add)
	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ok)
	ok, err = e.Enforce(sub, obj, act2)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(ok)
}

其中代码NewEnforcer函数会根据配置文件路径来返回一个Enforcer类型的指针。该指针下的Enforce方法即是经行授权处理,由于policy文档中’ylj’用户对于资源date1只有read权限,
在这里插入图片描述
所以对于第一个权限处理
在这里插入图片描述
返回true
而第二权限处理是ylj对date1经行write操作,
在这里插入图片描述
则会返回false

添加policy

官方提供了一个方法AddPolicy()可以直接添加policy,但发现此添加方法并不会改变policy.csv文档

func main() {
	e, err := casbin.NewEnforcer("./model.conf", "./policy.csv")
	if err != nil {
		fmt.Println(err)
		return
	}
	sub := "ylj"
	obj := "date1"
	act := "read"
	act2 := "write"
	add, err := e.AddPolicy(sub, obj, act2)
	if err != nil {
		return
	}
	fmt.Println("添加结果:", add)
	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("权限1:", ok)
	ok, err = e.Enforce(sub, obj, act2)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("权限2:", ok)
}

结果:
在这里插入图片描述
但发现policy.csv文件却没有改变
在这里插入图片描述
如果注销掉addPolicy方法则权限二会重新返回false。所以在实际使用中还是会结合适配器去使用casbin

结合gorm适配器使用

安装

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

初始化与mysql的连接

adapter, err := gormadapter.NewAdapter("mysql", "root:adss123@tcp(127.0.0.1:3306)/casbin", true)
	if err != nil {
		return
	}

其中连接函数最后的参数true是用于判断是否存在一个名为”casbin_rule“的表,若不存在则会自动去创建,若不想自动创建,则不需要设置该参数。
后续只用将该连接返回的变量代替policy的配置文件,正常初始化enforce即可

func main() {
   adapter, err := gormadapter.NewAdapter("mysql", "root:adss123@tcp(127.0.0.1:3306)/casbin", true)
   if err != nil {
   	return
   }
   e, err := casbin.NewEnforcer("model.conf", adapter)
   if err != nil {
   	return
   }
   sub := "ylj"
   obj := "date1"
   act := "read"
   ok, err := e.Enforce(sub, obj, act)
   if err != nil {
   	return
   }
   fmt.Println(ok)
}

对policy经行增删改查

官方已经给出了非常完善的指令文档,这里只展示一些基本指令。

AddPolicy()
AddPolicy 向当前策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true。

added := e.AddPolicy('eve', 'data3', 'read')

AddPolicies()
AddPolicy 向当前策略添加授权许多规则。 该操作本质上是原子的 因此,如果授权规则由不符合现行政策的规则组成, 函数返回false,当前政策中没有添加任何政策规则。 如果所有授权规则都符合政策规则,则函数返回true,每项政策规则都被添加到目前的政策中。

rules := [][] string {
    []string {"jack", "data4", "read"},
    []string {"katy", "data4", "write"},
    []string {"leyo", "data4", "read"},
    []string {"ham", "data4", "write"},
}

areRulesAdded := e.AddPolicies(rules)

RemovePolicy()
RemovePolicy 从当前策略中删除授权规则。

removed := e.RemovePolicy("alice", "data1", "read")

UpdatePolicy()
UpdatePolicy 把旧的政策更新到新的政策

updated, err := e.UpdatePolicy([]string{"eve", "data3", "read"}, []string{"eve", "data3", "write"})

GetPolicy
获取策略中的所有授权规则。

policy = e.GetPolicy()

不同于之前,这些指令再结合适配器后会在数据库内对policy进行修改,使其有更好的记录。

自定义匹配函数

首先准备函数。 它接受一些参数,然后返回一个布尔类型:
这里自定义一个超级管理员,若其名称为”*“则可以拥有所有用户的权限

func KeyMatch(key1 string, key2 string) bool {
   if key1 == "*" || key2 == "*" {
   	return true
   }
   return key1 == key2
}

然后用 interface{} 类型的接口包装它:

func KeyMatchFunc(args ...interface{}) (interface{}, error) {
    name1 := args[0].(string)
    name2 := args[1].(string)

    return (bool)(KeyMatch(name1, name2)), nil
}

最后,在Casbin的执行者(enforcer)中注册这个函数:

e.AddFunction("my_func", KeyMatchFunc)

这样就可以在model.conf中使用函数了
在这里插入图片描述

当前数据库中记录的policy为
在这里插入图片描述
执行代码

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	gormadapter "github.com/casbin/gorm-adapter/v3"
)

func main() {
	adapter, err := gormadapter.NewAdapter("mysql", "root:adss123@tcp(127.0.0.1:3306)/casbin", true)
	if err != nil {
		return
	}
	e, err := casbin.NewEnforcer("model.conf", adapter)
	if err != nil {
		return
	}
	e.AddFunction("my_func", KeyMatchFunc)

	sub := "*"
	obj := "data4"
	act := "read"
	ok, err := e.Enforce(sub, obj, act)
	if err != nil {
		return
	}
	fmt.Println(ok)
}
func KeyMatch(key1 string, key2 string) bool {
	if key1 == "*" || key2 == "*" {
		return true
	}
	return key1 == key2
}
func KeyMatchFunc(args ...interface{}) (interface{}, error) {
	name1 := args[0].(string)
	name2 := args[1].(string)

	return KeyMatch(name1, name2), nil
}

结果为
在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值