gin casbin xorm vue-admin权限认证。
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权限用到的一点看法,以及实际的步骤。