bee go融合Casbin RBAC权限控制
// 用户(user)
type User struct {
Id int `orm:"pk"`
Name string
Description string
RoleId string //1,2,3,4
}
// 角色(Role)
type Role struct {
Id int `orm:"pk"`
Name string //usecase/plan/index,tool/index/index
Description 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 coding
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, admin
我怕生讲理论会误导读者,所以我从beego+Casbin实现的角度来深入介绍。(因为我看Casbin文档之后,对各种名词介绍,方法的使用都知道了,但是写代码的时候却不知道怎么用)
一个请求进来,我设计的路线是这样走的
router/router.go
controller/index.go->Casbin
config/config.go->InitConfig
model/policy_models.go->GetEnforcer
model/beego_adapter.go->NewAdapter
controller/index.go->Check
// controller/index.go
package controllers
import (
"encoding/json"
"fmt"
"log"
"strings"
"uob/models"
"uob/conf"
"github.com/casbin/casbin"
)
type IndexController struct {
BaseController
}
// 定义权限数据结构、基于用户、角色、资源、操作的
type PermissonParam struct {
User string
Role string
Obj string
Act string
}
// 这是我自己测试用的模型,定义了一个路由名,一个传入的等待鉴权的信息模型
type Router struct {
Router string
Data PermissonParam
}
func (c *IndexController) Casbin(){
//conf.InitConfig调用的就是conf/config.go里面的一个方法,后面会把用到的文件主要部分
//./conf/casbin.toml指向的就是上面提到到的conf/casbin.toml
err := 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.RequestBody
router := &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.go
package conf
import (
"github.com/BurntSushi/toml"
)
type Config struct {
Casbin casbinConfig `toml:"casbin"`
}
type casbinConfig struct {
TenantRbacModelConf string `toml:"tenant_rbac_model_conf"`
}
var conf *Config
func GetConfig() *Config {
return conf
}
//toml配置规则,根据配置文件初始化结构体的值
func InitConfig(confPath string) error {
_, err := toml.DecodeFile(confPath, &conf)
return err
}
上文还提到了需要调用models/policy_models.go去创建casbin对象
package models
import (
"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, admin
a := NewAdapter()
//这里获取casbin的配置规则,也就是casbin.toml
cnf := conf.GetConfig()
m := casbin.NewModel(cnf.Casbin.TenantRbacModelConf)
e = casbin.NewEnforcer(m, a)
}
return e
}
走到这里,我们捋一遍之前我设计的路线,走到哪一步了
router/router.go-------------------------done
controller/index.go->Casbin--------------done
config/config.go->InitConfig-------------done
model/policy_models.go->GetEnforcer------done
model/beego_adapter.go->NewAdapter-------done
controller/index.go->Check
只差最后一步,就是真正鉴权的操作了,Check里面做了啥呢,按惯例贴代码
type allInfo struct {
Res bool
GetAllSubjects []string
GetAllObjects []string
GetAllActions []string
GetAllRoles []string
GetPolicy [][]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 read
result := e.Enforce(param.User, param.Obj, param.Act)
//这里会返回一个bool
c.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()
}
配置界面如下
欢迎扫码关注我~~