beego利用casbin进行权限管理——第二节 策略存储

beego利用casbin进行权限管理——第一节 起步、测试

beego利用casbin进行权限管理——第二节 策略存储

beego利用casbin进行权限管理——第三节 策略查询

beego利用casbin进行权限管理——第四节 策略更新

上一节讲到修改casbin目录下beego-orm-adapter里的adapter.go,这里要继续修改一下,否则会把数据库冲掉


接下来进行用户的角色分配。


代码如下,其实就是生成策略——把用户对应角色写入数据库casbin_rule

//添加用户角色
//先删除用户所有角色
func (c *RoleController) UserRole() {
	//要支持批量分配角色,循环用户id
	uid := c.GetString("uid") 
	//先删除用户的权限
	e.DeleteRolesForUser(uid) //下节介绍这个
	//再添加,如果没有选择,相当于删除了全部角色
	ids := c.GetString("ids") //roleid
	if ids != "" {
		array := strings.Split(ids, ",")
		for _, v1 := range array {
			e.AddGroupingPolicy(uid, "role_"+v1) //management_api.go
			//应该用AddRoleForUser()//rbac_api.go
		}
		// a.SavePolicy(e.GetModel())//autosave默认是true
		// 	[{0 p 12 1    } {0 g 8 1    } {0 g 7 1
		//    } {0 g 7 2    } {0 g 5 1    } {0 g 5 2    }]
		// lines := [7][4]string{{"0", "p", "100", "1"}, {"0", "p", "101", "1"}}
		// _, err := a.o.InsertMulti(len(lines), lines)
		// return err
	}
	c.Data["json"] = "ok"
	c.ServeJSON()
}
当然,前提是你先定义了角色。


数据库中的样子是这样的:


接下来是给角色赋予对某某目录下(v1/v2/v3/*)资源的各种权限。


conf目录下的rbac_model.conf如下

[request_definition]
r = sub, obj, act, suf 

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

[role_definition]
g = _, _

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

[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && regexMatch(r.suf, p.suf) && r.act == p.act

接下来给角色赋予项目目录下资源的请求权限,


代码:

//给角色赋项目目录的权限
func (c *RoleController) RolePermission() {
	roleids := c.GetString("roleids")
	rolearray := strings.Split(roleids, ",")
	// beego.Info(rolearray)
	permissionids := c.GetString("permissionids")
	permissionarray := strings.Split(permissionids, ",")
	// beego.Info(permissionarray)
	sufids := c.GetString("sufids")
	sufarray := strings.Split(sufids, ",")

	treeids := c.GetString("treeids") //项目目录id
	treearray := strings.Split(treeids, ",")
	// beego.Info(treearray)
	treenodeids := c.GetString("treenodeids") //项目目录的nodeid 0.0.0-0.0.1-0.1.0-0.1.0
	treenodearray := strings.Split(treenodeids, ",")
	// beego.Info(treenodearray)
	// treeids := c.GetString("tree")
	//json字符串解析到结构体,以便进行追加
	// var tree []Tree
	// err := json.Unmarshal([]byte(treeids), &tree)
	// if err != nil {
	// 	beego.Error(err)
	// }
	var success bool
	var nodeidint int
	var projurl, action, suf string
	var err error
	//取出项目目录的顶级
	var nodesid, nodesids []string
	if len(treenodearray) > 1 {
		nodesids, err = highest(treenodearray, nodesid, 0)
		if err != nil {
			beego.Error(err)
		}
	}
	// beego.Info(nodesids)
	for _, v1 := range rolearray {
		for _, v2 := range permissionarray {
			//定义读取、添加、修改、删除
			switch v2 {
			case "添加成果":
				action = "POST"
				suf = ".*"
			case "编辑成果":
				action = "UPDATE"
				suf = ".*"
			case "删除成果":
				action = "DELETE"
				suf = ".*"
			case "读取成果":
				action = "GET"
				for _, v4 := range sufarray {
					if v4 == "任意" {
						suf = ".*"
						break
					} else if v4 == "" { //用户没展开则读取不到table4的select
						suf = "(?i:pdf)"
					} else {
						suf = "(?i:" + v4 + ")"
					}
				}
			}

			for _, v3 := range nodesids {
				nodeidint, err = strconv.Atoi(v3)
				if err != nil {
					beego.Error(err)
				}
				//id转成64位
				pidNum, err := strconv.ParseInt(treearray[nodeidint], 10, 64)
				if err != nil {
					beego.Error(err)
				}

				//根据projid取出路径
				proj, err := m.GetProj(pidNum)
				if err != nil {
					beego.Error(err)
				}
				if proj.ParentIdPath == "" {
					projurl = "/" + strconv.FormatInt(proj.Id, 10) + "/*"
				} else {
					projurl = "/" + strings.Replace(proj.ParentIdPath, "-", "/", -1) + "/" + treearray[nodeidint] + "/*"
				}
				success = e.AddPolicy(v1, projurl, action, suf)
			}
		}
	}
	if success == true {
		c.Data["json"] = "ok"
	} else {
		c.Data["json"] = "wrong"
	}
	c.ServeJSON()
}

//迭代查出最高级的树状目录
func highest(nodeid []string, nodesid []string, i int) (nodesid1 []string, err error) {
	if i == 0 {
		nodesid = append(nodesid, "0")
	}
	var i1 int
	for i1 = i; i1 < len(nodeid)-1; i1++ {
		matched, err := regexp.MatchString("(?i:"+nodeid[i]+")", nodeid[i1+1])
		// fmt.Println(matched)
		if err != nil {
			beego.Error(err)
		}
		if !matched {
			i = i1 + 1
			nodesid = append(nodesid, strconv.Itoa(i1+1))
			break
		} else {
			if i == len(nodeid)-2 {
				return nodesid, err
			}
		}
	}
	if i1 < len(nodeid)-1 {
		nodesid, err = highest(nodeid, nodesid, i)
	}
	return nodesid, err
}
上面求树状目录最高级的目的是:我们选中某一级目录,其下级自动选中了,这些目录id传递到服务端后,我们只需要往策略里存入最高级的就行了,然后用keymatch匹配路由即可。比如,策略里运行访问v1/v2/*,那么,你访问v1/v2/v3/*也是可以的了。

查出用户的权限:

func (c *RoleController) Get() {
	id := c.Ctx.Input.Param(":id")
	c.Data["Id"] = id
	c.Data["Ip"] = c.Ctx.Input.IP()
	// if id == "" { //如果id为空,则查询
	roles, err := m.GetRoles()
	if err != nil {
		beego.Error(err)
	}

	if id != "" {
		//查出用户的角色,处于勾选状态
		userroles := e.GetRolesForUser(id)
		userrole := make([]Userrole, 0)
		var level string
		level = "2"
		for _, v1 := range roles {
			for _, v2 := range userroles {
				ridNum, err := strconv.ParseInt(v2, 10, 64)
				if err != nil {
					beego.Error(err)
				}
				if ridNum == v1.Id {
					level = "1"
				}
			}
			aa := make([]Userrole, 1)
			aa[0].Id = v1.Id
			aa[0].Rolename = v1.Rolename
			aa[0].Rolenumber = v1.Rolenumber
			aa[0].Level = level
			userrole = append(userrole, aa...)
			aa = make([]Userrole, 0)
			level = "2"
		}
		c.Data["json"] = userrole
		c.ServeJSON()
	}
	c.Data["json"] = roles
	c.ServeJSON()
}

 其他说明:

1.用户id和角色id重复问题。因为p策略可能对应的是user,也可能对应的role,所以当2者id相同的时候,就分不清是用户还是角色了。所以要给角色id前加role_字样。

(我这里存入的是用户id对应角色id,然后是角色对应的路由和请求权限,问题来了,当用户id和角色id相同的时候,即时这个用户不在角色里,这个用户直接具有了请求的权限,因为casbin不分辨用户和和角色。解决方案,是将角色id加上_role,比如1_role,在服务端再做处理。

这里讲用户和角色重名的问题。
// Create Role and User 
// Casbin only stores the user-role mapping. 
 // Do not use the same name for a user and a role inside a RBAC system, 
 // because Casbin recognizes users and roles as strings, and there&#39;s no way for 
 // Casbin to know whether you are specifying user alice or role alice. 
 // You can simply solve it by using role_alice. 
https://github.com/luk4z7/middleware-acl/blob/9764e0c0bcc1804272921c34da0148caa2448d63/src/middleware/service/role/role.go#L19-L22

2.只需要addpolicy(实时存入map)就行了,而不需要savepolicy(存入数据库),因为自动存入数据库了。这里讲自动存入数据库,好处是避免了重复存储。

https://github.com/casbin/casbin/wiki/AutoSave

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hotqin888/article/details/78571240
文章标签: casbin EngineerCMS beego
个人分类: golang beego casbin
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭