casbin一个支持ACL, RBAC, ABAC的开源方案,github地址在https://github.com/casbin/casbin
作者有开发beego的适配器,在https://github.com/casbin/beego-orm-adapter,但是如果使用该适配器,只能单独建一个数据库,如果跟其他表公用一个数据库,将导致原来的表被刷丢。
为此需要一定的改动,具体是:
1 添加一个新初始化适配器的函数:
func NewDefaultAdapter() *Adapter{
a := &Adapter{}
a.driverName = ""
a.dataSourceName = ""
a.dbSpecified = true
a.o = orm.NewOrm()
return a
}
2 修改dropTable,这样会保存规则的时候只会刷掉规则表casbin_rule
func (a *Adapter) dropTable() {
o :=orm.NewOrm()
_, err := o.Raw("drop table casbin_rule").Exec()
if err != nil {
panic(err)
}
}
3 修改createTable,自动建表时使用了默认数据库连接
func (a *Adapter) createTable() {
err := orm.RunSyncdb("default", false, true)
if err != nil {
panic(err)
}
}
完整代码如下:
package casbinA
import (
"errors"
"runtime"
"strings"
"github.com/astaxie/beego/orm"
"github.com/casbin/casbin/model"
"github.com/casbin/casbin/persist"
"github.com/lib/pq"
"github.com/casbin/casbin"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego"
"betterServer/util"
"strconv"
)
var(
perm *Adapter
e *casbin.Enforcer
)
type CasbinRule struct {
Id int
PType string
V0 string
V1 string
V2 string
V3 string
V4 string
V5 string
}
func init() {
orm.RegisterModel(new(CasbinRule))
}
// Adapter represents the Xorm adapter for policy storage.
type Adapter struct {
driverName string
dataSourceName string
dbSpecified bool
o orm.Ormer
}
// finalizer is the destructor for Adapter.
func finalizer(a *Adapter) {
}
// NewAdapter is the constructor for Adapter.
// dbSpecified is an optional bool parameter. The default value is false.
// It's up to whether you have specified an existing DB in dataSourceName.
// If dbSpecified == true, you need to make sure the DB in dataSourceName exists.
// If dbSpecified == false, the adapter will automatically create a DB named "casbin".
func NewAdapter(driverName string, dataSourceName string, dbSpecified ...bool) *Adapter {
a := &Adapter{}
a.driverName = driverName
a.dataSourceName = dataSourceName
if len(dbSpecified) == 0 {
a.dbSpecified = false
} else if len(dbSpecified) == 1 {
a.dbSpecified = dbSpecified[0]
} else {
panic(errors.New("invalid parameter: dbSpecified"))
}
// Open the DB, create it if not existed.
a.open()
// Call the destructor when the object is released.
runtime.SetFinalizer(a, finalizer)
return a
}
func NewDefaultAdapter() *Adapter{
a := &Adapter{}
a.driverName = ""
a.dataSourceName = ""
a.dbSpecified = true
a.o = orm.NewOrm()
return a
}
func (a *Adapter) registerDataBase(aliasName, driverName, dataSource string, params ...int) error {
err := orm.RegisterDataBase(aliasName, driverName, dataSource, params...)
if err != nil && strings.HasSuffix(err.Error(), "already registered, cannot reuse") {
return nil
}
return err
}
func (a *Adapter) createDatabase() error {
var err error
var o orm.Ormer
if a.driverName == "postgres" {
err = a.registerDataBase("create_casbin", a.driverName, a.dataSourceName + " dbname=postgres")
} else {
err = a.registerDataBase("create_casbin", a.driverName, a.dataSourceName)
}
if err != nil {
return err
}
o = orm.NewOrm()
if a.driverName == "postgres" {
if _, err = o.Raw("CREATE DATABASE casbin").Exec(); err != nil {
// 42P04 is duplicate_database
if err.(*pq.Error).Code == "42P04" {
return nil
}
}
} else {
_, err = o.Raw("CREATE DATABASE IF NOT EXISTS casbin").Exec()
}
return err
}
func (a *Adapter) open() {
var err error
if a.dbSpecified {
err = a.registerDataBase("casbin", a.driverName, a.dataSourceName)
if err != nil {
panic(err)
}
} else {
if err = a.createDatabase(); err != nil {
panic(err)
}
if a.driverName == "postgres" {
err = a.registerDataBase("casbin", a.driverName, a.dataSourceName + " dbname=casbin")
} else {
err = a.registerDataBase("casbin", a.driverName, a.dataSourceName + "casbin")
}
if err != nil {
panic(err)
}
}
a.o = orm.NewOrm()
a.o.Using("casbin")
//不需要建表,在main函数中已经操作了
//a.createTable()
}
func (a *Adapter) close() {
a.o = nil
}
func (a *Adapter) createTable() {
err := orm.RunSyncdb("default", false, true)
if err != nil {
panic(err)
}
}
func (a *Adapter) dropTable() {
o :=orm.NewOrm()
_, err := o.Raw("drop table casbin_rule").Exec()
if err != nil {
panic(err)
}
}
func loadPolicyLine(line CasbinRule, model model.Model) {
lineText := line.PType
if line.V0 != "" {
lineText += ", " + line.V0
}
if line.V1 != "" {
lineText += ", " + line.V1
}
if line.V2 != "" {
lineText += ", " + line.V2
}
if line.V3 != "" {
lineText += ", " + line.V3
}
if line.V4 != "" {
lineText += ", " + line.V4
}
if line.V5 != "" {
lineText += ", " + line.V5
}
persist.LoadPolicyLine(lineText, model)
}
// LoadPolicy loads policy from database.
func (a *Adapter) LoadPolicy(model model.Model) error {
var lines []CasbinRule
_, err := a.o.QueryTable("casbin_rule").All(&lines)
if err != nil {
return err
}
for _, line := range lines {
loadPolicyLine(line, model)
}
return nil
}
func savePolicyLine(ptype string, rule []string) CasbinRule {
line := CasbinRule{}
line.PType = ptype
if len(rule) > 0 {
line.V0 = rule[0]
}
if len(rule) > 1 {
line.V1 = rule[1]
}
if len(rule) > 2 {
line.V2 = rule[2]
}
if len(rule) > 3 {
line.V3 = rule[3]
}
if len(rule) > 4 {
line.V4 = rule[4]
}
if len(rule) > 5 {
line.V5 = rule[5]
}
return line
}
// SavePolicy saves policy to database.
func (a *Adapter) SavePolicy(model model.Model) error {
//不能清掉数据库
//删掉casbin_rule
a.dropTable()
a.createTable()
var lines []CasbinRule
for ptype, ast := range model["p"] {
for _, rule := range ast.Policy {
line := savePolicyLine(ptype, rule)
lines = append(lines, line)
}
}
for ptype, ast := range model["g"] {
for _, rule := range ast.Policy {
line := savePolicyLine(ptype, rule)
lines = append(lines, line)
}
}
_, err := a.o.InsertMulti(len(lines), lines)
return err
}
// AddPolicy adds a policy rule to the storage.
func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error {
line := savePolicyLine(ptype, rule)
_, err := a.o.Insert(&line)
return err
}
// RemovePolicy removes a policy rule from the storage.
func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error {
line := savePolicyLine(ptype, rule)
_, err := a.o.Delete(&line, "p_type", "v0", "v1", "v2", "v3", "v4", "v5")
return err
}
// RemoveFilteredPolicy removes policy rules that match the filter from the storage.
func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error {
line := CasbinRule{}
line.PType = ptype
filter := []string{}
filter = append(filter, "p_type")
if fieldIndex <= 0 && 0 < fieldIndex + len(fieldValues) {
line.V0 = fieldValues[0 - fieldIndex]
filter = append(filter, "v0")
}
if fieldIndex <= 1 && 1 < fieldIndex + len(fieldValues) {
line.V1 = fieldValues[1 - fieldIndex]
filter = append(filter, "v1")
}
if fieldIndex <= 2 && 2 < fieldIndex + len(fieldValues) {
line.V2 = fieldValues[2 - fieldIndex]
filter = append(filter, "v2")
}
if fieldIndex <= 3 && 3 < fieldIndex + len(fieldValues) {
line.V3 = fieldValues[3 - fieldIndex]
filter = append(filter, "v3")
}
if fieldIndex <= 4 && 4 < fieldIndex + len(fieldValues) {
line.V4 = fieldValues[4 - fieldIndex]
filter = append(filter, "v4")
}
if fieldIndex <= 5 && 5 < fieldIndex + len(fieldValues) {
line.V5 = fieldValues[5 - fieldIndex]
filter = append(filter, "v5")
}
_, err := a.o.Delete(&line, filter...)
return err
}
func PermInit(){
perm = NewDefaultAdapter()
m := casbin.NewModel();
m.AddDef("r", "r", "sub, obj, act");
m.AddDef("p", "p", "sub, obj, act");
m.AddDef("e", "e", "some(where (p.eft == allow))");
m.AddDef("m", "m", "r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act)");
e = casbin.NewEnforcer("conf/rbac.conf", perm,true)
// Load the policy from DB.
err := e.LoadPolicy()
if err!=nil{
beego.Debug("Perm err:",err)
}
Check the permission.
//e.Enforce("alice", "data1", "read")
//
Modify the policy.
e.AddPolicy(...)
e.RemovePolicy(...)
//
Save the policy back to DB.
//e.SavePolicy()
}
func PermCheckFilter(ctx *context.Context) {
if ctx.Request.URL.String() == "/v1/admin/auth/login"{
return
}
user_info:=ctx.Input.GetData("user_info").(util.TokenInfo)
sub := strconv.FormatInt(user_info.UserId,10) // the user that wants to access a resource.
obj := ctx.Request.URL.String() // the resource that is going to be accessed.
act := ctx.Request.Method // the operation that the user performs on the resource.
beego.Debug("user:",sub,"obj:",obj,"act:",act)
if e.Enforce(sub, obj, act) == true {
// permit alice to read data1
beego.Debug("accept user:",sub,"obj:",obj,"act:",act)
//ctx.Output.SetStatus(code)
//ctx.Output.JSON(jso, false, false)
} else {
// deny the request, show an error
beego.Debug("deny user:",sub,"obj:",obj,"act:",act)
jso := util.H{"status": false, "reason": "permisson deny!", "data": util.H{}}
ctx.Output.SetStatus(401)
ctx.Output.JSON(jso, false, false)
}
}
func GetEnforcer()*casbin.Enforcer{
return e
}