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權限用到的一點看法,以及實際的步驟。
