背景:

        针对应用中可能会存在权限的要求,就是存在不同的角色分别要有不同的权限。下面大概介绍一下相关的知识:

学习网址:

权限模型分类

权限类型权限作用
ACL访问控制列表
RBAC基于角色的权限控制,(优势易于扩展,用户确认角色,然后根据角色划分不同的权限)
ABAC基于属性的权限控制
PBAC基于策略的权限控制

1.  概念:(Casbin是基于RBAC设计模型实现的)

Casbin 是一个强大的,开源的访问控制框架,权限管理机制支持多种访问控制模型; 并且支持多种编程语言; 

2. 规则介绍:

[request_definition]
    r = sub, obj, act
# 请求的规则 
# r 是规则的名称,sub 为请求的实体,obj 为资源的名称, act 为请求的实际操作动作
[policy_definition]
    p = sub, obj, act
# 策略的规则 
# 同请求
[role_definition]
    g = _, _
# 角色的定义
# g 角色的名称,第一个位置为用户,第二个位置为角色,第三个位置为域(在多租户场景下使用)
[policy_effect]
    e = some(where (p.eft == allow))
# 任意一条 policy rule 满足, 则最终结果为 allow
[matchers]
    m = g(r.sub, p.sub) == true \
            && keyMatch2(r.obj, p.obj) == true \
            && regexMatch(r.act, p.act) == true \
            || r.sub == "root"
# 前三个用来匹配上面定义的请求的规则, 最后一个或条件为:如果实体是root 直接通过, 不验证权限

r: 表示请求的规则。sub请求实体,obj资源路径,act请求的方法。

p: 表示协议的协议的规则,一般会放在数据库表中,r请求会和p进行比较,来判断是否有权限进行访问。

g:表示角色定义,也就是说简单点就是将用户A定义为角色B。(一共两个参数)

m:表示匹配的规则。这里就是进行整个匹配的核心,直接得出结论是否有权限进行相关的API权限。

m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) 

这种方式是REST风格是支持模糊查询的,也就是说数据库表中如何obj为: /read/*

那么请求的时候只要是/read/...下面的请求都会支持认证。

注意下面实例中m采用的方式不支持模糊匹配。

其中: r.sub == "root" 表示请求用户为root 的时候,默认不认证,拥有所有的权限。

也可以添加这种  || r.obj in ('data2', 'data3'),表示请求体中当请求为data2或者data3的时候,会自动的不进行认证,表示该请求为开放API。

实例如下:

首先创建项目拉取需要的相关依赖包:

"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v2"

发现使用go get方式获取会失败,所以我们将会采用mod的方式进行相关依赖包的下载使用,具体的话请参照,我的博客:

下面分部进行讲解:

第一部分就是基于Casbin的API函数进行搭建库书写方式如下,会自动的在数据库的/目录下面自动的创建一个casbin数据库

和一张表casbin_rule(协议表,里面的数据后面需要添加,可以手动添加,也可以根据业务自动的创建可配置化的页面进行的创建-(开源项目go-admin就是基于这种配置方式))

关于匹配的规则这里面采用的是下面的方式,也可以存放在文本中就行读取文本就可以了,关于具体的表示上面的讲解中已经进行了相关的说明。

 

casbin.NewEnforcer(m, a)这个函数是最重要的也就是直接将数据库中的casbin_rule和匹配规则进行绑定生成相关的实施体,进行后续的权限匹配工作。
	a, err := xormadapter.NewAdapter("mysql", "root:560298@tcp(127.0.0.1:3306)/")
	if err != nil {
		log.Fatalf("error: adapter: %s", err)
	}

	m, err := model.NewModelFromString(`
[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 && r.obj == p.obj && r.act == p.act  || r.sub == "root"
`)
	if err != nil {
		log.Fatalf("error: model: %s", err)
	}

	e, err := casbin.NewEnforcer(m, a)
	if err != nil {
		log.Fatalf("error: enforcer: %s", err)
	}

第二部分:就是接着上面获得e实施体来讲的,就是具体的权限匹配工作。流程就是

ok, err := e.Enforce(sub, obj, act) 函数会将sub主体, obj实例和act函数进行匹配,然后遍历匹配,成功就会返回true否则就是false。就可以在对应的地方进行确认匹配成功的业务逻辑实现,和不成功的错误返回业务。
	/**
	进行相关的校验Casbin进行路径p协议验证
	*/
	sub := "admin" // the user that wants to access a resource.
	obj := "/read" // the resource that is going to be accessed.
	act := "act"   // the operation that the user performs on the resource.

	ok, err := e.Enforce(sub, obj, act)

	if err != nil {
		// handle err
		fmt.Println("异常")
	}

	if ok == true {
		fmt.Println("成功匹配")
		// permit alice to read data1
	} else {
		fmt.Println("匹配失败")
		// deny the request, show an error
	}

第三部分:我们采用的不是g用户匹配方式,

e.GetRolesForUser("test") 该函数是获得协议数据库中所有用户名为test的角色数组。
ok, err = e.Enforce(roles[0], obj, act) 该函数就是和第二部分一样用户进行比配,
	roles, _ := e.GetRolesForUser("test")
	for k, v := range roles {
		fmt.Println("test的第", k+1, "个角色为:", v)
	}

	ok, err = e.Enforce(roles[0], obj, act)
	if ok == true {
		fmt.Println("成功匹配")
		// permit alice to read data1
	} else {
		fmt.Println("匹配失败")
		// deny the request, show an error
	}

最后一部分就是数据库表的表的数据,这里都是手动添加的。可以随意的进行相关的添加修改,最好的方式就是开发一个CRUD页面实现该表的增删改查业务:

附录,下面是测试所有的代码:

package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	xormadapter "github.com/casbin/xorm-adapter/v2"
	_ "github.com/go-sql-driver/mysql"
	"log"
)

func main() {
	a, err := xormadapter.NewAdapter("mysql", "root:560298@tcp(127.0.0.1:3306)/")
	if err != nil {
		log.Fatalf("error: adapter: %s", err)
	}

	m, err := model.NewModelFromString(`
[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 && r.obj == p.obj && r.act == p.act  || r.sub == "root"
`)
	if err != nil {
		log.Fatalf("error: model: %s", err)
	}

	e, err := casbin.NewEnforcer(m, a)
	if err != nil {
		log.Fatalf("error: enforcer: %s", err)
	}
	/**
	进行相关的校验Casbin进行路径p协议验证
	*/
	sub := "admin" // the user that wants to access a resource.
	obj := "/read" // the resource that is going to be accessed.
	act := "act"   // the operation that the user performs on the resource.

	ok, err := e.Enforce(sub, obj, act)

	if err != nil {
		// handle err
		fmt.Println("异常")
	}

	if ok == true {
		fmt.Println("成功匹配")
		// permit alice to read data1
	} else {
		fmt.Println("匹配失败")
		// deny the request, show an error
	}

	roles, _ := e.GetRolesForUser("test")
	for k, v := range roles {
		fmt.Println("test的第", k+1, "个角色为:", v)
	}

	ok, err = e.Enforce(roles[0], obj, act)
	if ok == true {
		fmt.Println("成功匹配")
		// permit alice to read data1
	} else {
		fmt.Println("匹配失败")
		// deny the request, show an error
	}
}

第四部分,知识扩展:

API管理的信息获取: e为上面的e, err := casbin.NewEnforcer(m, a) 执行者实体。

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

GetAllNamedActions 获取当前命名策略中显示的操作列表。

。。。。。

还有很多API管理的权限获取,请参考官网: