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权限用到的一点看法,以及实际的步骤。

浙公网安备 33010602011771号