1、因為用到是gin所以直接指定rbac吧。
一般都是使用:角色,操作路徑,操作類型來做權限。如果還要弄更多,這里就不涉及更多了
首先配置文件
[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) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")
2、從數據庫加載角色配置
package main import ( "github.com/casbin/casbin/v2" _ "github.com/lib/pq" "github.com/casbin/xorm-adapter" ) func main() { // Initialize a Xorm adapter and use it in a Casbin enforcer: // The adapter will use the Postgres database named "casbin". // If it doesn't exist, the adapter will create it automatically. a, _ := xormadapter.NewAdapter("postgres", "user=postgres_username password=postgres_password host=127.0.0.1 port=5432 sslmode=disable") // Your driver and data source. // Or you can use an existing DB "abc" like this: // The adapter will use the table named "casbin_rule". // If it doesn't exist, the adapter will create it automatically. // a := xormadapter.NewAdapter("postgres", "dbname=abc user=postgres_username password=postgres_password host=127.0.0.1 port=5432 sslmode=disable", true) e, _ := casbin.NewEnforcer("../examples/rbac_model.conf", a) // Load the policy from DB. e.LoadPolicy() }
上面這個是官方提供的從數據加載權限配置的案例。里面會給自動生成數據庫,自動生成表。
那么作為一個要集成到自己項目里面的功能,有一個表跟自己的表。。。nb_開頭太格格不入了。所以可以拿它出來進行改寫。
// Copyright 2017 The casbin Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package utils import ( "errors" model2 "notebooks/model" "runtime" "strings" "github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/persist" "github.com/lib/pq" "github.com/xormplus/xorm" ) // Adapter represents the Xorm adapter for policy storage. type Adapter struct { driverName string dataSourceName string dbSpecified bool engine *xorm.Engine } // finalizer is the destructor for Adapter. func finalizer(a *Adapter) { err := a.engine.Close() if err != nil { panic(err) } } // 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() (*Adapter, error) { a := &Adapter{} // Open the DB, create it if not existed. err := a.open() if err != nil { return nil, err } // Call the destructor when the object is released. runtime.SetFinalizer(a, finalizer) return a, nil } //通過已有的engine生成 func NewAdapterByEngine(engine *xorm.Engine) (*Adapter, error) { a := &Adapter{ engine: engine, } err := a.createTable() if err != nil { return nil, err } return a, nil } //生成數據庫 func (a *Adapter) createDatabase() (err error) { if a.driverName == "postgres" { if _, err = AppEngine.Exec("CREATE DATABASE casbin"); err != nil { // 42P04 is duplicate_database if pqerr, ok := err.(*pq.Error); ok && pqerr.Code == "42P04" { AppEngine.Close() return nil } } } else if a.driverName != "sqlite3" { _, err = AppEngine.Exec("CREATE DATABASE IF NOT EXISTS casbin") } if err != nil { AppEngine.Close() return err } return AppEngine.Close() } func (a *Adapter) open() error { a.engine = AppEngine return a.createTable() } func (a *Adapter) close() error { err := a.engine.Close() if err != nil { return err } a.engine = nil return nil } func (a *Adapter) createTable() error { return a.engine.Sync2(new(model2.NbCasbinRule)) } func (a *Adapter) dropTable() error { return a.engine.DropTables(new(model2.NbCasbinRule)) } //原生加載一條權限 func loadPolicyLine(line *model2.NbCasbinRule, model model.Model) { const prefixLine = ", " var sb strings.Builder sb.WriteString(line.PType) if len(line.V0) > 0 { sb.WriteString(prefixLine) sb.WriteString(line.V0) } if len(line.V1) > 0 { sb.WriteString(prefixLine) sb.WriteString(line.V1) } if len(line.V2) > 0 { sb.WriteString(prefixLine) sb.WriteString(line.V2) } if len(line.V3) > 0 { sb.WriteString(prefixLine) sb.WriteString(line.V3) } if len(line.V4) > 0 { sb.WriteString(prefixLine) sb.WriteString(line.V4) } if len(line.V5) > 0 { sb.WriteString(prefixLine) sb.WriteString(line.V5) } persist.LoadPolicyLine(sb.String(), model) } // 從數據庫查詢權限 func (a *Adapter) LoadPolicy(model model.Model) error { var lines []*model2.NbCasbinRule if err := a.engine.Find(&lines); err != nil { return err } //一條一條地加載 for _, line := range lines { loadPolicyLine(line, model) } return nil } //這個是配置一條權限,返回一條數據 func savePolicyLine(ptype string, rule []string, colId int) *model2.NbCasbinRule { line := &model2.NbCasbinRule{PType: ptype} l := len(rule) if l > 0 { line.V0 = rule[0] } if l > 1 { line.V1 = rule[1] } if l > 2 { line.V2 = rule[2] } if l > 3 { line.V3 = rule[3] } if l > 4 { line.V4 = rule[4] } if l > 5 { line.V5 = rule[5] } line.ColId=colId return line } // 這個是將文件里寫好的配置文件,寫入數據庫不能使用 func (a *Adapter) SavePolicy(model model.Model) error { if 1==1{ return errors.New("不能使用本方法") } return nil /* err := a.dropTable() if err != nil { return err } err = a.createTable() if err != nil { return err } var lines []*model2.NbCasbinRule 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.engine.Insert(&lines) return err */ } // 添加一條權限,但是只是添加,沒有入配置的,要重新使用loadPolicy(看情況處理,后期可能要做一下性能處理,再加載) func (a *Adapter) AddPolicyNew(sec string, ptype string, rule []string,colId int) error { line := savePolicyLine(ptype, rule,colId) _, err := a.engine.Insert(line) return err } // 添加一條權限,但是只是添加,沒有入配置的,要重新使用loadPolicy(看情況處理,后期可能要做一下性能處理,再加載) func (a *Adapter) AddPolicy(sec string, ptype string, rule []string) error { line := savePolicyLine(ptype, rule,0) _, err := a.engine.Insert(line) return err } // 刪除一條權限,同樣沒有入配置(看情況處理,后期可能要做一下性能處理,再加載) func (a *Adapter) RemovePolicyNew(sec string, ptype string, rule []string,colId int) error { line := savePolicyLine(ptype, rule,colId) _, err := a.engine.Delete(line) return err } // 刪除一條權限,同樣沒有入配置(看情況處理,后期可能要做一下性能處理,再加載) func (a *Adapter) RemovePolicy(sec string, ptype string, rule []string) error { line := savePolicyLine(ptype, rule,0) _, err := a.engine.Delete(line) return err } // 根據配置刪除相應的一條權限(少用吧),同樣要重新加載 func (a *Adapter) RemoveFilteredPolicy(sec string, ptype string, fieldIndex int, fieldValues ...string) error { if 1==1{ return errors.New("不能使用本方法") } line := &model2.NbCasbinRule{PType: ptype} idx := fieldIndex + len(fieldValues) if fieldIndex <= 0 && idx > 0 { line.V0 = fieldValues[0-fieldIndex] } if fieldIndex <= 1 && idx > 1 { line.V1 = fieldValues[1-fieldIndex] } if fieldIndex <= 2 && idx > 2 { line.V2 = fieldValues[2-fieldIndex] } if fieldIndex <= 3 && idx > 3 { line.V3 = fieldValues[3-fieldIndex] } if fieldIndex <= 4 && idx > 4 { line.V4 = fieldValues[4-fieldIndex] } if fieldIndex <= 5 && idx > 5 { line.V5 = fieldValues[5-fieldIndex] } _, err := a.engine.Delete(line) return err }
在這里,直接把里面的生成結構寫成與自己風格一致的struct,並放在model里,這樣,它就不會生成原版的表,而是生成model里的表了,在里面使用到的engine可以使用自己全局的定義的db engine。保證配置數據庫連接只有一個地方。
里面的方法基本上根據自己的業務寫在自己的service里,並不一定要用它原來的寫法。
3、到這里,已經可以從數據庫里加載自己想要的權限了。那么,要怎么放入權限呢
casbinrule原表有,ptype,v0,v1,v2,v3,v4,v5.目前只使用到ptype,v0,v1,v2
ptype寫死為p,v0為角色,v1為操作路徑v2為方式,如:ptype:p,v0:supperAdmin,v1:/money/all_in_my_card,v2:POST
說明supperAdmin有路徑/money/all_in_my_card操作post的權限
根據自己的業務要求,把對應的數據生成放到表后。下面使用中間件來進行驗證
func CheckPermission() gin.HandlerFunc { return func(c *gin.Context) { //根據上下文獲取載荷claims 從claims獲得role strClaims, has := c.Get("claims") if has { claims := strClaims.(*CustomClaims) role := claims.Role if len(role) < 3 { c.JSON(http.StatusOK, gin.H{ "errno": 403, "errmsg": "驗證權限出錯", }) c.Abort() return } rolsString := role[1 : len(role)-1] roleArr := strings.Split(rolsString, ",") var fitErr error var has bool var err error for _, oneRole := range roleArr { has, err = utils.CasbinEn.Enforce( oneRole, c.Request.URL.Path, c.Request.Method) if err != nil { fitErr = err break } if has { break } } if fitErr != nil { c.JSON(http.StatusOK, gin.H{ "errno": 403, "errmsg": "驗證權限出錯", }) c.Abort() return } if has { c.Next() } else { c.JSON(http.StatusOK, gin.H{ "errno": 403, "msg": "很抱歉您沒有此權限", }) c.Abort() return } } else { c.Next() } } }
4、上面是處理好的后台權限的控制。(需搭配jwt驗證一起使用)。那么vue-admin里怎么控制呢.
vue-admin有一個perms的權限處理。在后台生成"perms":["GET /sys/admin","GET /sys/log","GET /sys/os","GET /sys/role"]給到前台就行。
其實就是根據用戶的角色,查詢casbinrule里的v1,v2,組成json list返回就可以。
上面是這段時間改寫casbin權限用到的一點看法,以及實際的步驟。