go语言使用Casbin实现角色的权限控制

Casbin是用于Golang项目的功能强大且高效的开源访问控制库。本文主要介绍了go语言使用Casbin实现角色的权限控制,感兴趣的可以了解下

本文主要介绍了go语言使用Casbin实现角色的权限控制,分享给大家,具体如下:

在这里插入图片描述

介绍 Casbin 是什么?

官方解释:Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型。

Casbin只负责访问控制。身份认证 authentication(即验证用户的用户名、密码),需要其他专门的身份认证组件负责。例如(jwt-go)

两个核心概念:

访问控制模型model和策略policy。

工作原理:

Casbin把 访问控制模型 被抽象为基于

PERM元模型 (Policy, Effect, Request, Matchers) [策略,效果,请求,匹配器], 反映了权限的本质 – 访问控制

  • Policy: 定义权限的规则
  • Effect: 定义组合了多个 Policy 之后的结果, allow/deny
  • Request: 访问请求, 也就是谁想操作什么
  • Matcher: 判断 Request 是否满足 Policy

匹配来源:定义的 request 和 存储的 police 比对判断。

所有 model file 中主要就是定义 PERM 4 个部分的一个文件( model.conf , 这个文件一般是固定的)。

因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型(ACL, RBAC, ABAC)。

例如,您可以在一个model中获得RBAC角色和ABAC属性,并共享一组policy规则。

g, g2, g3 表示不同的 RBAC 体系, _, _ 表示用户和角色 _, _, _ 表示用户, 角色, 域(也就是租户)

rbac_models.conf

# Request definition
[request_definition]
r = sub, obj, act

# Policy definition
[policy_definition]
p = sub, obj, act

# Policy effect
[policy_effect]
e = some(where (p.eft == allow))

# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

实战

一般运用到项目后台的管理权限中

初始化数据库

初始化环境后,系统会在你选择的数据库自动生成一个casbin_rule的数据库表

在这里插入图片描述

用户权限的增删改查

tips:这些接口,需要放在截器中间件前面, 不然空表无法添加数据

//使用自定义拦截器中间件
r.Use(Authorize())
//增加policy
    r.POST("/api/v1/add", func(c *gin.Context) {
        fmt.Println("增加Policy")
        if ok, _ := Enforcer.AddPolicy("admin", "/api/v1/world", "GET"); !ok {
            fmt.Println("Policy已经存在")
        } else {
            fmt.Println("增加成功")
        }
    })
    //删除policy
    r.DELETE("/api/v1/delete", func(c *gin.Context) {
        fmt.Println("删除Policy")
        if ok, _ := Enforcer.RemovePolicy("admin", "/api/v1/world", "GET"); !ok {
            fmt.Println("Policy不存在")
        } else {
            fmt.Println("删除成功")
        }
    })
    //获取policy
    r.GET("/api/v1/get", func(c *gin.Context) {
        fmt.Println("查看policy")
        list := Enforcer.GetPolicy()
        for _, vlist := range list {
            for _, v := range vlist {
                fmt.Printf("value: %s, ", v)
            }
        }
    })

添加权限,访问其添加接口

在这里插入图片描述

单个用户保存权限的路由是多条记录

在这里插入图片描述

在使用Casbin中间件时,会访问表中的数据库表 ,得到相应的权限信息,增加访问信息后,访问接口成功

在这里插入图片描述

代码

package main

import (
    "fmt"
    "github.com/casbin/casbin"
    xormadapter "github.com/casbin/xorm-adapter"
    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
    "log"
    "net/http"
)

var Enforcer *casbin.Enforcer

func init() {
    CasbinSetup()
}

// 初始化casbin
func CasbinSetup(){
    a, err := xormadapter.NewAdapter("mysql", "root:root123@tcp(127.0.0.1:3306)/casbin?charset=utf8", true)
    if err != nil {
        log.Printf("连接数据库错误: %v", err)
        return
    }
    e, err := casbin.NewEnforcer("./conf/rbac_models.conf", a)
    if err != nil {
        log.Printf("初始化casbin错误: %v", err)
        return
    }

    Enforcer = e
    //return e
}


func Hello(c *gin.Context) {
    fmt.Println("Hello 接收到GET请求..")
    c.JSON(http.StatusOK, gin.H{
        "code": 200,
        "msg":  "Success",
        "data": "Hello 接收到GET请求..",
    })
}


func main() {
    //获取router路由对象
    r := gin.New()


    //增加policy
    r.GET("/api/v1/add", func(c *gin.Context) {
        fmt.Println("增加Policy")
        if ok, _ := Enforcer.AddPolicy("admin", "/api/v1/world", "GET"); !ok {
            fmt.Println("Policy已经存在")
        } else {
            fmt.Println("增加成功")
        }
    })
    //删除policy
    r.DELETE("/api/v1/delete", func(c *gin.Context) {
        fmt.Println("删除Policy")
        if ok, _ := Enforcer.RemovePolicy("admin", "/api/v1/world", "GET"); !ok {
            fmt.Println("Policy不存在")
        } else {
            fmt.Println("删除成功")
        }
    })
    //获取policy
    r.GET("/api/v1/get", func(c *gin.Context) {
        fmt.Println("查看policy")
        list := Enforcer.GetPolicy()
        for _, vlist := range list {
            for _, v := range vlist {
                fmt.Printf("value: %s, ", v)
            }
        }
    })

    //使用自定义拦截器中间件
    r.Use(Authorize())




    //创建请求
    r.GET("/api/v1/hello", Hello)
    r.Run(":9000") //参数为空 默认监听8080端口
}




//拦截器
func Authorize() gin.HandlerFunc {

    return func(c *gin.Context) {
        //var e *casbin.Enforcer
        e := Enforcer

        //从DB加载策略
        e.LoadPolicy()

        //获取请求的URI
        obj := c.Request.URL.RequestURI()
        //获取请求方法
        act := c.Request.Method
        //获取用户的角色 应该从db中读取
        sub := "admin"

        //判断策略中是否存在
        if ok, _ := e.Enforce(sub, obj, act); ok {
            fmt.Println("恭喜您,权限验证通过")
            c.Next() // 进行下一步操作
        } else {
            fmt.Println("很遗憾,权限验证没有通过")
            c.Abort()
        }
    }
}

总代码

package main

import (
    "fmt"
    "github.com/casbin/casbin"
    xormadapter "github.com/casbin/xorm-adapter"
    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
    "log"
    "net/http"
)

var Enforcer *casbin.Enforcer

func init() {
    CasbinSetup()
}

// 初始化casbin
func CasbinSetup(){
    a, err := xormadapter.NewAdapter("mysql", "root:root123@tcp(127.0.0.1:3306)/casbin?charset=utf8", true)
    if err != nil {
        log.Printf("连接数据库错误: %v", err)
        return
    }
    e, err := casbin.NewEnforcer("./conf/rbac_models.conf", a)
    if err != nil {
        log.Printf("初始化casbin错误: %v", err)
        return
    }

    Enforcer = e
    //return e
}


func Hello(c *gin.Context) {
    fmt.Println("Hello 接收到GET请求..")
    c.JSON(http.StatusOK, gin.H{
        "code": 200,
        "msg":  "Success",
        "data": "Hello 接收到GET请求..",
    })
}


func main() {
    //获取router路由对象
    r := gin.New()


    //增加policy
    r.GET("/api/v1/add", func(c *gin.Context) {
        fmt.Println("增加Policy")
        if ok, _ := Enforcer.AddPolicy("admin", "/api/v1/world", "GET"); !ok {
            fmt.Println("Policy已经存在")
        } else {
            fmt.Println("增加成功")
        }
    })
    //删除policy
    r.DELETE("/api/v1/delete", func(c *gin.Context) {
        fmt.Println("删除Policy")
        if ok, _ := Enforcer.RemovePolicy("admin", "/api/v1/world", "GET"); !ok {
            fmt.Println("Policy不存在")
        } else {
            fmt.Println("删除成功")
        }
    })
    //获取policy
    r.GET("/api/v1/get", func(c *gin.Context) {
        fmt.Println("查看policy")
        list := Enforcer.GetPolicy()
        for _, vlist := range list {
            for _, v := range vlist {
                fmt.Printf("value: %s, ", v)
            }
        }
    })

    //使用自定义拦截器中间件
    r.Use(Authorize())




    //创建请求
    r.GET("/api/v1/hello", Hello)
    r.Run(":9000") //参数为空 默认监听8080端口
}




//拦截器
func Authorize() gin.HandlerFunc {

    return func(c *gin.Context) {
        //var e *casbin.Enforcer
        e := Enforcer

        //从DB加载策略
        e.LoadPolicy()

        //获取请求的URI
        obj := c.Request.URL.RequestURI()
        //获取请求方法
        act := c.Request.Method
        //获取用户的角色 应该从db中读取
        sub := "admin"

        //判断策略中是否存在
        if ok, _ := e.Enforce(sub, obj, act); ok {
            fmt.Println("恭喜您,权限验证通过")
            c.Next() // 进行下一步操作
        } else {
            fmt.Println("很遗憾,权限验证没有通过")
            c.Abort()
        }
    }
}

封装后的代码

package mycasbin

import (
    "simple-api/mongoose"
    "github.com/casbin/mongodb-adapter"
    "github.com/casbin/casbin"
    "gopkg.in/mgo.v2/bson"
)

type CasbinModel struct {
    ID       bson.ObjectId `json:"id" bson:"_id"`
    Ptype    string        `json:"ptype" bson:"ptype"`
    RoleName string        `json:"rolename" bson:"v0"`
    Path     string        `json:"path" bson:"v1"`
    Method   string        `json:"method" bson:"v2"`
}

//添加权限
func (c *CasbinModel) AddCasbin(cm CasbinModel) bool {
    e := Casbin()
    e.AddPolicy(cm.RoleName, cm.Path, cm.Method)
    return true

}

//持久化到数据库
// "github.com/casbin/mongodb-adapter"
// func Casbin() *casbin.Enforcer {
//     a := mongodbadapter.NewAdapter(mongoose.MongoUrl)
//     e := casbin.NewEnforcer("conf/auth_model.conf", a)
//     e.LoadPolicy()
//     return e
// }
func Casbin() *casbin.Enforcer {
    a := mongodbadapter.NewAdapter(mongoose.MongoUrl)
    e, err := casbin.NewEnforcer("conf/auth_model.conf", a)
    if err != nil {
        panic(err)
    }
    e.LoadPolicy()
    return e
}
package apis

import (
    "net/http"
    "simple-api/utils/mycasbin"

    "github.com/gin-gonic/gin"
    "gopkg.in/mgo.v2/bson"
)

var (
    casbins = mycasbin.CasbinModel{}
)

func AddCasbin(c *gin.Context) {
    rolename := c.PostForm("rolename")
    path := c.PostForm("path")
    method := c.PostForm("method")
    ptype := "p"
    casbin := mycasbin.CasbinModel{
        ID:       bson.NewObjectId(),
        Ptype:    ptype,
        RoleName: rolename,
        Path:     path,
        Method:   method,
    }
    isok := casbins.AddCasbin(casbin)
    if isok {
        c.JSON(http.StatusOK, gin.H{
            "success": true,
            "msg":     "保存成功",
        })
    } else {
        c.JSON(http.StatusOK, gin.H{
            "success": false,
            "msg":     "保存失败",
        })
    }
}

参考

https://www.jianshu.com/p/9506406e745f