bee go融合Casbin RBAC权限控制
// 用户(user)type User struct {Id int `orm:"pk"`Name stringDescription stringRoleId string //1,2,3,4}// 角色(Role)type Role struct {Id int `orm:"pk"`Name string //usecase/plan/index,tool/index/indexDescription string}
简单介绍一下Casbin的工作方式,访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个模型。anyway,这个不重要
// casbin.toml 文中是通过字符串赋值给tenant_rbac_model_conf的方式// 把规则写入Csbin模型的[casbin]tenant_rbac_model_conf = '''[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'''
//casbin.policy 这个文件其实是存在于数据库的,这里只是展示一些,不需要hard codingp, charger, apply/work, readp, admin, apply/usecase, readp, charger, apply/totest, readg, zhaofx, chargerp, admin, apply/task, readg, baoyx, admin
我怕生讲理论会误导读者,所以我从beego+Casbin实现的角度来深入介绍。(因为我看Casbin文档之后,对各种名词介绍,方法的使用都知道了,但是写代码的时候却不知道怎么用)
一个请求进来,我设计的路线是这样走的
router/router.gocontroller/index.go->Casbinconfig/config.go->InitConfigmodel/policy_models.go->GetEnforcermodel/beego_adapter.go->NewAdaptercontroller/index.go->Check
// controller/index.gopackage controllersimport ("encoding/json""fmt""log""strings""uob/models""uob/conf""github.com/casbin/casbin")type IndexController struct {BaseController}// 定义权限数据结构、基于用户、角色、资源、操作的type PermissonParam struct {User stringRole stringObj stringAct string}// 这是我自己测试用的模型,定义了一个路由名,一个传入的等待鉴权的信息模型type Router struct {Router stringData PermissonParam}func (c *IndexController) Casbin(){//conf.InitConfig调用的就是conf/config.go里面的一个方法,后面会把用到的文件主要部分//./conf/casbin.toml指向的就是上面提到到的conf/casbin.tomlerr := conf.InitConfig("./conf/casbin.toml")if err != nil {log.Fatal(err)}//调用model/policy_models.go里面的方法去创建casbin对象var e = models.GetEnforcer()//接收post传入的json参数并赋值给Router模型data := c.Ctx.Input.RequestBodyrouter := &Router{}jsonerr := json.Unmarshal(data, router)if jsonerr != nil {fmt.Println("json.Unmarshal is err:", jsonerr.Error())}switch strings.Trim(router.Router," ") {case "check":fmt.Printf("check\n" )Check(c, e, router.Data)...default:fmt.Printf("nothing to do\n" )}}
上面提到的config.go文件内容如下,使用了toml库文件格式加载配置项
//conf/config.gopackage confimport ("github.com/BurntSushi/toml")type Config struct {Casbin casbinConfig `toml:"casbin"`}type casbinConfig struct {TenantRbacModelConf string `toml:"tenant_rbac_model_conf"`}var conf *Configfunc GetConfig() *Config {return conf}//toml配置规则,根据配置文件初始化结构体的值func InitConfig(confPath string) error {_, err := toml.DecodeFile(confPath, &conf)return err}
上文还提到了需要调用models/policy_models.go去创建casbin对象
package modelsimport ("uob/conf""github.com/casbin/casbin")// 租户(tenant),定义自己需要的字段,对应的是数据库的字段type Tenant struct {...}// 角色(role)type Role struct {...}// 用户(user)type User struct {...}// 资源(resource)type Resource struct {...}// 操作(action)type Action struct {...}// Policy 策略type CasbinRule struct {Id string `orm:"pk"`PType string `json:"pType"`V0 string `json:"v0"`V1 string `json:"v1"`V2 string `json:"v2"`V3 string `json:"v3"`V4 string `json:"v4"`V5 string `json:"v5"`}var e *casbin.Enforcer//创建casbin对象func GetEnforcer() *casbin.Enforcer {if e == nil {//调用beego_adapter.go里面的方法,主要做的事情是加载数据库里面的配置规则//生成类似这样的数据// p, charger, apply/work, read// p, admin, apply/usecase, read// p, charger, apply/totest, read// g, zhaofx, charger// p, admin, apply/task, read// g, baoyx, admina := NewAdapter()//这里获取casbin的配置规则,也就是casbin.tomlcnf := conf.GetConfig()m := casbin.NewModel(cnf.Casbin.TenantRbacModelConf)e = casbin.NewEnforcer(m, a)}return e}
走到这里,我们捋一遍之前我设计的路线,走到哪一步了
router/router.go-------------------------donecontroller/index.go->Casbin--------------doneconfig/config.go->InitConfig-------------donemodel/policy_models.go->GetEnforcer------donemodel/beego_adapter.go->NewAdapter-------donecontroller/index.go->Check
只差最后一步,就是真正鉴权的操作了,Check里面做了啥呢,按惯例贴代码
type allInfo struct {Res boolGetAllSubjects []stringGetAllObjects []stringGetAllActions []stringGetAllRoles []stringGetPolicy [][]string}/**检查权限curl -X POST -H "Content-Type: application/json" -d '{"router":"check","data":{"User":"zhaofx","Role":"charger","obj":"apply/totest","act":"read"}}' "http://127.0.0.1:8080/casbin"*/func Check(c *IndexController, e *casbin.Enforcer, param PermissonParam) {//e里面有开放很多Api,可以直接调用GetAllSubjects := e.GetAllSubjects()GetAllObjects := e.GetAllObjects()GetAllActions := e.GetAllActions()GetAllRoles := e.GetAllRoles()GetPolicy := e.GetPolicy()fmt.Println(param.User, param.Obj, param.Act)//zhaofx apply/totest readresult := e.Enforce(param.User, param.Obj, param.Act)//这里会返回一个boolc.Data["json"] = Response{0, "success", allInfo{result,GetAllSubjects,GetAllObjects,GetAllActions,GetAllRoles,GetPolicy,}}c.ServeJSON()}
篇幅问题,设置权限的逻辑不过多介绍了,具体实现如下
/*给角色添加资源的访问权限curl -X POST -H "Content-Type: application/json" -d '{"router":"AddPolicy","data":{"User":"zhaofx","Role":"charger","obj":"apply/totest","act":"read"}}' "http://127.0.0.1:8080/casbin"这里会成一条以p开头的casbin.policy记录,含义就是这这个角色有哪个资源的操作权限*/func AddPolicy(c *IndexController, e *casbin.Enforcer, param PermissonParam) {if !e.AddPolicy(param.User, param.Obj, param.Act) {c.Data["json"] = Response{0, "success", "add policy failed!"}c.ServeJSON()}c.Data["json"] = Response{0, "success", "success"}c.ServeJSON()}/*把用户和角色做关联curl -X POST -H "Content-Type: application/json" -d '{"router":"AddRoleForUser","data":{"User":"zhaofx","Role":"charger","obj":"apply/totest","act":"read"}}' "http://127.0.0.1:8080/casbin"这里会成一条以g开头的casbin.policy记录,含义就是这个用户属于哪个角色*/func AddRoleForUser(c *IndexController, e *casbin.Enforcer, param PermissonParam) {fmt.Println(param)if !e.AddRoleForUser(param.User, param.Role) {c.Data["json"] = Response{0, "success", "create group in tenant failed!"}c.ServeJSON()}c.Data["json"] = Response{0, "success", "success"}c.ServeJSON()}
配置界面如下
欢迎扫码关注我~~
