Casbin权限模型

权限框架casbin

1.概述

Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。

Casbin支持以下编程语言:

在这里插入图片描述

  • Casbin可以做到:
  1. 支持自定义请求的格式,默认的请求格式为{subject, object, action}
  2. 具有访问控制模型model和策略policy两个核心概念。
  3. 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。
  4. 支持超级用户,如 rootAdministrator,超级用户可以不受授权策略的约束访问任意资源。
  5. 支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo*
  • Casbin不能做到:
  1. 身份认证 authentication(即验证用户的用户名、密码),casbin只负责访问控制。应该有其他专门的组件负责身份认证,然后由casbin进行访问控制,二者是相互配合的关系。
  2. 管理用户列表或角色列表。 Casbin 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系

github:https://github.com/casbin/casbin

docs:https://casbin.org/docs/zh-CN/overview

2.工作原理

在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。

PERM模型

PERM(Policy, Effect, Request, Matchers)模型很简单, 但是反映了权限的本质 – 访问控制

  • Policy: 定义权限的规则
  • Effect: 定义组合了多个 Policy 之后的结果, allow/deny
  • Request: 访问请求, 也就是谁想操作什么
  • Matcher: 判断 Request 是否满足 Policy

[外链图片转存失败(img-ys2XIpxc-1565944147357)(C:\Users\Todd\AppData\Roaming\Typora\typora-user-images\1565593950617.png)]

Module File 语法

casbin 是基于 PERM 的, 所有 model file 中主要就是定义 PERM 4 个部分

1.Request definition

[request_definition]
r = sub, obj, act

​ 分别表示 request 中的

  • accessing entity (Subject)
  • accessed resource (Object)
  • the access method (Action)

2.Policy definition

[policy_definition]
p = sub, obj, act
p2 = sub, act

定义的每一行称为 policy rule, p, p2 是 policy rule 的名字. p2 定义的是 sub 所有的资源都能执行 act

3.Policy effect

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

上面表示有任意一条 policy rule 满足, 则最终结果为 allow

4.Matchers

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

定义了 request 和 policy 匹配的方式, p.eft 是 allow 还是 deny, 就是基于此来决定的

5.Role

[role_definition]
g = _, _
g2 = _, _
g3 = _, _, _

g, g2, g3 表示不同的 RBAC 体系, _, _ 表示用户和角色 _, _, _ 表示用户, 角色, 域

您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中获得RBAC角色和ABAC属性,并共享一组policy规则。

Casbin中最基本、最简单的model是ACL。ACL中的model CONF为:

# Request definition
[request_definition]
r = sub, obj, act

# Policy definition
[policy_definition]
p = sub, obj, act

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

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

ACL model的示例policy如下:

p, alice, data1, read
p, bob, data2, write

这表示:

  • alice可以读取data1
  • bob可以编写data2

Model存储

  • 从 .CONF 文件中加载 model
e := casbin.NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
  • 从代码加载 model
// Initialize the model from Go code.
m := casbin.NewModel()
m.AddDef("r", "r", "sub, obj, act")
m.AddDef("p", "p", "sub, obj, act")
m.AddDef("g", "g", "_, _")
m.AddDef("e", "e", "some(where (p.eft == allow))")
m.AddDef("m", "m", "g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act")

// Load the policy rules from the .CSV file adapter.
// 使用自己的 adapter 替换。
a := persist.NewFileAdapter("examples/rbac_policy.csv")

// 创建一个 enforcer。
e := casbin.NewEnforcer(m, a)
  • 从字符串加载的 model(推荐)
// Initialize the model from a string.
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 = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
`
m := NewModel(text)

// Load the policy rules from the .CSV file adapter.
// Replace it with your adapter to avoid files.
a := persist.NewFileAdapter("examples/rbac_policy.csv")

// Create the enforcer.
e := casbin.NewEnforcer(m, a)

这种方法的优点是您不需要维护模型文件

Casbin API

//全局变量 e是执行者实例
e := NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")

//获取当前策略中显示的主题列表
allSubjects := e.GetAllSubjects()

//获取当前命名策略中显示的主题列表
allNamedSubjects := e.GetAllNamedSubjects("p")

//获取当前策略中显示的对象列表
allObjects := e.GetAllObjects()

//获取当前命名策略中显示的对象列表
allNamedObjects := e.GetAllNamedObjects("p")

//获取当前策略中显示的操作列表
allActions := e.GetAllActions()

//获取当前命名策略中显示的操作列表
allNamedActions := e.GetAllNamedActions("p")

//获取当前策略中显示的角色列表
allRoles = e.GetAllRoles()

//获取当前命名策略中显示的角色列表
allNamedRoles := e.GetAllNamedRoles("g")

//获取策略中的所有授权规则
policy = e.GetPolicy()

//获取策略中的所有授权规则,可以指定字段筛选器
filteredPolicy := e.GetFilteredPolicy(0, "alice")

//获取命名策略中的所有授权规则
namedPolicy := e.GetNamedPolicy("p")

//获取命名策略中的所有授权规则,可以指定字段过滤器
filteredNamedPolicy = e.GetFilteredNamedPolicy("p", 0, "bob")

//获取策略中的所有角色继承规则
groupingPolicy := e.GetGroupingPolicy()

//获取策略中的所有角色继承规则,可以指定字段筛选器
filteredGroupingPolicy := e.GetFilteredGroupingPolicy(0, "alice")

//获取策略中的所有角色继承规则
namedGroupingPolicy := e.GetNamedGroupingPolicy("g")

//获取策略中的所有角色继承规则
namedGroupingPolicy := e.GetFilteredNamedGroupingPolicy("g", 0, "alice")

// 确定是否存在授权规则
hasPolicy := e.HasPolicy("data2_admin", "data2", "read")

//确定是否存在命名授权规则
hasNamedPolicy := e.HasNamedPolicy("p", "data2_admin", "data2", "read")

//向当前策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddPolicy("eve", "data3", "read")

// 向当前命名策略添加授权规则。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddNamedPolicy("p", "eve", "data3", "read")

// 从当前策略中删除授权规则
removed := e.RemovePolicy("alice", "data1", "read")

// 移除当前策略中的授权规则,可以指定字段筛选器。 RemovePolicy 从当前策略中删除授权规则
removed := e.RemoveFilteredPolicy(0, "alice", "data1", "read")

//从当前命名策略中删除授权规则
removed := e.RemoveNamedPolicy("p", "alice", "data1", "read")

//从当前命名策略中移除授权规则,可以指定字段筛选器
removed := e.RemoveFilteredNamedPolicy("p", 0, "alice", "data1", "read")

//确定是否存在角色继承规则
has := e.HasGroupingPolicy("alice", "data2_admin")

//确定是否存在命名角色继承规则
has := e.HasNamedGroupingPolicy("g", "alice", "data2_admin")

// 向当前策略添加角色继承规则。 如果规则已经存在,函数返回false,并且不会添加规则。 如果规则已经存在,函数返回false,并且不会添加规则
added := e.AddGroupingPolicy("group1", "data2_admin")

//将命名角色继承规则添加到当前策略。 如果规则已经存在,函数返回false,并且不会添加规则。 否则,函数通过添加新规则并返回true
added := e.AddNamedGroupingPolicy("g", "group1", "data2_admin")

// 从当前策略中删除角色继承规则
removed := e.RemoveGroupingPolicy("alice", "data2_admin")

//从当前策略中移除角色继承规则,可以指定字段筛选器
removed := e.RemoveFilteredGroupingPolicy(0, "alice")

//从当前命名策略中移除角色继承规则
removed := e.RemoveNamedGroupingPolicy("g", "alice")

//当前命名策略中移除角色继承规则,可以指定字段筛选器
removed := e.RemoveFilteredNamedGroupingPolicy("g", 0, "alice")

//添加自定义函数
func CustomFunction(key1 string, key2 string) bool {
    if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data/:resource" {
        return true
    } else if key1 == "/alice_data2/myid/using/res_id" && key2 == "/alice_data2/:id/using/:resId" {
        return true
    } else {
        return false
    }
}

func CustomFunctionWrapper(args ...interface{}) (interface{}, error) {
    key1 := args[0].(string)
    key2 := args[1].(string)

    return bool(CustomFunction(key1, key2)), nil
}

e.AddFunction("keyMatchCustom", CustomFunctionWrapper)

casbin的使用者

casbin-go语言使用者

名称描述模型策略
VMware HarborVMware的开源可信云本地注册表项目,用于存储、签名和扫描内容。CodeCode
Intel RMD英特尔的资源管理守护进程。.conf.csv
VMware Dispatch用于部署和管理无服务器风格应用程序的框架。CodeCode
Skydive一个开源的实时网络拓扑和协议分析器。Code.csv
Zenpress用Golang编写的CMS系统。.confGorm
Argo CD为Kubernetes持续提供的GitOps。.conf.csv
Muxi Cloud一种更容易管理Kubernetes集群的方法。.confCode
EngineerCMSCMS管理工程师的知识。.confSQLite
Cyber Auth API一个Golang身份验证API项目.conf.csv
IRIS CommunityIRIS社区活动网页.conf.csv
Metadata DBBB归档元数据数据库。.conf.csv
Qilin API游戏内容下的ProtocolONE许可证管理工具。Code.csv

3.权限系统设计模型分析

常见的设计模式(DAC,MAC,RBAC,ABAC)

自主访问控制(DAC: Discretionary Access Control)

系统会识别用户,然后根据被操作对象(Subject)的权限控制列表(ACL: Access Control List)或者权限控制矩阵(ACL: Access Control Matrix)的信息来决定用户的是否能对其进行哪些操作,例如读取或修改。

而拥有对象权限的用户,又可以将该对象的权限分配给其他用户,所以称之为“自主(Discretionary)”控制。

这种设计最常见的应用就是文件系统的权限设计,如微软的NTFS。

DAC最大缺陷就是对权限控制比较分散,不便于管理,比如无法简单地将一组文件设置统一的权限开放给指定的一群用户

在这里插入图片描述

强制访问控制(MAC: Mandatory Access Control)

MAC是为了弥补DAC权限控制过于分散的问题而诞生的。在MAC的设计中,每一个对象都都有一些权限标识,每个用户同样也会有一些权限标识,而用户能否对该对象进行操作取决于双方的权限标识的关系,这个限制判断通常是由系统硬性限制的。比如在影视作品中我们经常能看到特工在查询机密文件时,屏幕提示需要“无法访问,需要一级安全许可”,这个例子中,文件上就有“一级安全许可”的权限标识,而用户并不具有。

MAC非常适合机密机构或者其他等级观念强烈的行业,但对于类似商业服务系统,则因为不够灵活而不能适用。

在这里插入图片描述

基于角色的访问控制(RBAC: Role-Based Access Control)

因为DAC和MAC的诸多限制,于是诞生了RBAC,并且成为了迄今为止最为普及的权限设计模型。

RBAC在用户和权限之间引入了“角色(Role)”的概念(暂时忽略Session这个概念):

[外链图片转存失败(img-49TXB2He-1565944147363)(C:\Users\Todd\AppData\Roaming\Typora\typora-user-images65593099489.png)]

如图所示,每个用户关联一个或多个角色,每个角色关联一个或多个权限,从而可以实现了非常灵活的权限管理。角色可以根据实际业务需求灵活创建,这样就省去了每新增一个用户就要关联一遍所有权限的麻烦。简单来说RBAC就是:用户关联角色,角色关联权限。另外,RBAC是可以模拟出DAC和MAC的效果的。

基于属性的权限验证(ABAC: Attribute-Based Access Control)

ABAC被一些人称为是权限系统设计的未来。

不同于常见的将用户通过某种方式关联到权限的方式,ABAC则是通过动态计算一个或一组属性来是否满足某种条件来进行授权判断(可以编写简单的逻辑)。属性通常来说分为四类:用户属性(如用户年龄),环境属性(如当前时间),操作属性(如读取)和对象属性(如一篇文章,又称资源属性),所以理论上能够实现非常灵活的权限控制,几乎能满足所有类型的需求。

例如规则:“允许所有班主任在上课时间自由进出校门”这条规则,其中,“班主任”是用户的角色属性,“上课时间”是环境属性,“进出”是操作属性,而“校门”就是对象属性了。为了实现便捷的规则设置和规则判断执行,ABAC通常有配置文件(XML、YAML等)或DSL配合规则解析引擎使用。

在这里插入图片描述

用户在携带自身的属性值包括主题属性,资源属性,环境属性,然后向资源发送请求,授权引擎 会根据subject所携带的属性进行判断,然后会给出拒绝或者同意的结果给用户,然后就可以访问资源。

总结一下,ABAC有如下特点:

  1. 集中化管理
  2. 可以按需实现不同颗粒度的权限控制
  3. 不需要预定义判断逻辑,减轻了权限系统的维护成本,特别是在需求经常变化的系统中
  4. 定义权限时,不能直观看出用户和对象间的关系
  5. 规则如果稍微复杂一点,或者设计混乱,会给管理者维护和追查带来麻烦
  6. 权限判断需要实时执行,规则过多会导致性能问题

既然ABAC这么好,那最流行的为什么还是RBAC呢?

大部分系统对权限控制并没有过多的需求,而且ABAC的管理相对来说太复杂了。

ABAC有时也被称为PBAC(Policy-Based Access Control)或CBAC(Claims-Based Access Control)

4.百度超级链权限系统

wiki地址:https://github.com/xuperchain/xuperunion/wiki/2.-关键概念#26-账户权限控制模型

名词解释

  • AK(Access Key):具体的一个address,由密码学算法生成一组公私钥对,然后将公钥用指定编码方式压缩为一个地址。

  • 账号(Account): 在超级链上部署合约需要有账号, 账号可以绑定一组AK(公钥),并且AK可以有不同的权重。 账户的名字具有唯一性。

模型介绍

系统会首先识别用户,然后根据被操作对象的ACL的信息来决定用户能否对其进行哪些操作

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

[外链图片转存失败(img-7RsY44Ym-1565944147369)(C:\Users\Todd\AppData\Roaming\Typora\typora-user-images65925943358.png)]

在超级链中,账户和合约的关系如下所述:

  • 个人账户AK:是指一个具体的地址Addr;
    • 用户的创建是离线的行为,可以通过命令行工具或者API进行创建
  • 合约账户:超级链智能合约的管理单元。
    • 账户的创建:
      • 任何账户或者AK都可以调用系统级智能合约创建账户;
      • 创建账户需要指定账户对应的拥有者的地址集,如果一个账户中只有一个地址, 那么这个Addr对账户完全控制;
      • 创建账户需要指定ACL控制策略,用于账户其他管理动作的权限控制;
      • 创建账户需要消耗账户资源;
    • 账户名命名规则;
      • 中间为长度为16位字符串;
      • 以"XC"开头;
      • 以"@链名"结尾;
    • 账户管理:依地址集合据创建时指定的地址集和权限策略,管理账户其他操作;
      • 账户股东剔除和加入;
      • 账户资产转账;
      • 创建合约,创建智能合约需要消耗账户资源,先将utxo资源打到账户下,通过消耗账户的utxo资源创建合约,验证的逻辑需要走账户的ACL控制;
      • 合约Method权限模型管理;
  • 智能合约:超级链中的一个具体的合约,属于某个账户;
    • 账户所属人员允许在账户内部署合约;
    • 账户所属人员可以定义合约管理的权限模型;
    • 设置合约方法的权限模型,合约内有一个权限表,记录:{ contract.method,permission_model}
  • 合约命名规则:长度为4~16个字符(包括4和16),首字母可选项为[ a-ZA-Z_ ],末尾字符可选项为[ a-zA-Z0-9_ ],中间部分的字符可选项为[ a-zA-Z_. ]
account := "XC0000000000000001@xuper"
ak := "XC0000000000000001@xuper/dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"

系统功能和设计

主要有两个功能:账号权限管理、合约权限管理

  1. 账号权限管理 账号的创建、添加和删除AK、设置AK权重、权限模型
  2. 合约权限管理 设置合约调用权限, 支持2种权限模型: a. 背书阈值:在名单中的AK或Account签名且他们的权重值加起来超过一定阈值,就可以调用合约 b. AK集合: 定义多组AK集合,集合内的AK需要全部签名,集合间只要有一个集合有全部签名即可

系统设计

[外链图片转存失败(img-A4akN9Ln-1565944147372)(C:\Users\Todd\AppData\Roaming\Typora\typora-user-images65926347212.png)]

权限策略:

var PermissionRule_name = map[int32]string{
	0: "NULL",          // 无权限控制
	1: "SIGN_THRESHOLD",// 签名阈值策略
	2: "SIGN_AKSET",    // AKSet签名策略
	3: "SIGN_RATE",     // 签名率策略
	4: "SIGN_SUM",      // 签名个数策略
	5: "CA_SERVER",     // CA服务器鉴权
	6: "COMMUNITY_VOTE",// 社区治理
}
//签名阈值策略:
  Sum{Weight(AK_i) , if sign_ok(AK_i)}  >=  acceptValue

权限测试例:

func Test_IdentifyAccount(t *testing.T) {
	account := "XC0000000000000001@xuper"
	ak := "XC0000000000000001@xuper/dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"
	pubkey := "{\"Curvname\":\"P-256\",\"X\":74695617477160058757747208220371236837474210247114418775262229497812962582435,\"Y\":51348715319124770392993866417088542497927816017012182211244120852620959209571}"
	prikey := "{\"Curvname\":\"P-256\",\"X\":74695617477160058757747208220371236837474210247114418775262229497812962582435,\"Y\":51348715319124770392993866417088542497927816017012182211244120852620959209571,\"D\":29079635126530934056640915735344231956621504557963207107451663058887647996601}"
	msg := "this is the test message from permission"

	xcc, err := crypto_client.CreateCryptoClientFromJSONPublicKey([]byte(pubkey))
	if err != nil {
		t.Error("create crypto client failed, err=", err)
		return
	}

	ecdsaPriKey, err := xcc.GetEcdsaPrivateKeyFromJSON([]byte(prikey))
	if err != nil {
		t.Error("get private key failed, err=", err)
		return
	}

	sign, err := xcc.SignECDSA(ecdsaPriKey, []byte(msg))
	if err != nil {
		t.Error("sign failed, err=", err)
		return
	}

	signInfo := &pb.SignatureInfo{
		Sign:      sign,
		PublicKey: pubkey,
	}

	aks := make([]string, 1)
	signs := make([]*pb.SignatureInfo, 1)

	aks[0] = ak
	signs[0] = signInfo
	aclMgr, err := acl_mock.NewFakeACLManager()
	if err != nil {
		t.Error("NewAclManager failed, err=", err)
		return
	}
	pm := &pb.PermissionModel{
		Rule:        pb.PermissionRule_SIGN_THRESHOLD,
		AcceptValue: 1,
	}
	aclObj := &pb.Acl{
		Pm:        pm,
		AksWeight: make(map[string]float64),
	}

	aclObj.AksWeight["dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"] = 1
	aclMgr.SetAccountACL(account, aclObj)
    //主要为了实现IdentifyAccount方法
    //SetAccountAC/ IdentifyAccount检查aks和符号是否匹配,这些aks可以通过account的ACL策略表示       给定的帐户。如果签名匹配aks,并且aks可以代表帐户,则返回true。将账户acl保存在内存中
	result, err := IdentifyAccount(account, aks, signs, []byte(msg), aclMgr)
	if err != nil || !result {
		t.Error("IdentifyAccount failed, err=", err)
		return
	}

	fakesign := "this is a fake signature"
	signInfo2 := &pb.SignatureInfo{
		Sign:      []byte(fakesign),
		PublicKey: pubkey,
	}

	signs[0] = signInfo2
	result, err = IdentifyAccount(account, aks, signs, []byte(msg), aclMgr)
	if result == true {
		t.Error("IdentifyAccount fake sign failed, result=", result)
	}
}

在IdentifyAccount方法中主要实现:

func IdentifyAccount(account string, aksuri []string, signs []*pb.SignatureInfo,
	msg []byte, aclMgr acl.ManagerInterface) (bool, error) {
	akslen := len(aksuri)
	signslen := len(signs)
	// aks and signs could have zero length for permission rule Null
	if akslen != signslen || aclMgr == nil {
		return false, fmt.Errorf("Invalid Param, akslen=%d,signslen=%d,aclMgr=%v", akslen, signslen, aclMgr)
	}

	// build perm tree
	pnode, err := ptree.BuildAccountPermTree(aclMgr, account, aksuri, signs)
	if err != nil {
		return false, err
	}

	return validatePermTree(pnode, msg, true)
}

在这其中主要实现buildAccountPermTree和validatePermTree两个方法,Pnode定义了perm 树的节点

type PermNode struct {
   Name     string            // the name(id) of account/ak/method
   ACL      *pb.Acl           // the ACL definition of this account/method
   Status   ValidateStatus    // the ACL validation status of this node
   SignInfo *pb.SignatureInfo // the signature info of this node, only AK have this                                  field
   Children []*PermNode       // the children of this node, usually are ACL members                                of account/method
}

const (
	_ ValidateStatus = iota
	// NotVerified : not verified by ACLValidator
	NotVerified
	// Success : ACLValidator verified successful
	Success
	// Failed : ACLValidator verified failed
	Failed
)

它也没有使用PERM模型,至于为什么会用Perm这个词,目前也没想明白。

5.ABAC 模型示例

package main

import (
	"fmt"
	"github.com/casbin/casbin"
	"github.com/casbin/casbin/model"
)
const modelText = `
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj,act

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

[matchers]
m = r.sub.App == r.obj.App &&  r.sub.Type == r.obj.Type &&  r.sub.Method == r.obj.Method  &&  r.sub.Name == r.obj.Name \
        &&  r.sub.Read == r.act.Read &&  r.sub.Write == r.act.Write
`

type User struct {
	Id       int
	UserName string
	Group    []Group
}
type Group struct {
	Id       int
	Name     string
	Read     bool
	Write    bool
	App      string // app
	Type     string // 类型
	Method   string // 方法
	Priority int    // 优先级
}

type Obj struct {
	App    string // app
	Name  string
	Type   string // 类型
	Method string // 方法
}

type Act struct {
	Read  bool
	Write bool
}



func main() {
	m := model.Model{}
	m.LoadModelFromText(modelText)
	e:= casbin.NewEnforcer(m)
	group1 := Group{
		Name:     "lk",
		Read:     true,
		Write:    true,
		App:      "asset",
		Type:     "user",
		Method:   "Get",
		Priority: 100,
	}
    //group2  中Name和Write 不与Obj资源和执行操作权限中相同
	group2 := Group{
		Name:     "lkTest",
		Read:     true,
		Write:    false,
		App:      "asset",
		Type:     "user",
		Method:   "Get",
		Priority: 100,
	}

	//  用户 palletone  属于 group1 , group2
	user1 := User{
		UserName: "palletone",
		Group:    []Group{group1, group2},
	}
    //资源属性
	obj := Obj{
		App:    "asset",
		Name:  "lk",
		Type:   "user",
		Method: "Get",
	}
    //当执行操作权限 Read  Write为True时  策略执行结果为allow
	act := Act{
		Read:true,
		Write:true,
	}
	// 检查 用户 palletone 所有的组  是否有权限
	for _, v := range user1.Group {
		//强制决定一个“subject”是否可以通过操作“action”访问一个“object”,输入参数通常是:(sub, obj, act)。
		flag:= e.Enforce(v, obj, act)
		if flag {
			fmt.Println("权限正常")
		} else {
			fmt.Println("没有权限")
		}
	}

}

//打印结果

//权限正常
//没有权限

版权声明:本文为lk2684753原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。