如何在项目中加入casbin进行简单的权限验证

2024-03-10 14:02:05 浏览数 (3)

前言

casbin是目前流行的身份鉴定工具之一,笔者在近期写的一个项目中也使用到了casbin对于项目的权限进行鉴定,于是在此分享一下笔者是如何使用casbin进行权限判定的。

注意:以下权限验证方式以golang gin gorm为例

安装

安装casbin直接以官网示例进行即可,在此给出官网网址:https://casbin.org/docs/get-started

如果需要将策略存储至数据库,则可以使用对应的Adapter,在此给出Adapter官网网址:https://casbin.org/docs/adapters

如笔者使用的是go Gorm,选择对应的Adapter即可

模型选择

因为要进行权限验证以及动态修改用户权限,在此选择casbin的RBAC模型,具体配置文件如下

代码语言:conf复制
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act, eft

[role_definition]
g = _, _

[policy_effect]
e = some(where (p.eft == allow)) && !some(where (p.eft == deny))

[matchers]
m = g(r.sub, p.sub) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj)

其中keyMatch()为官方提供的函数,具体匹配方法如下

代码语言:go复制
func KeyMatch(key1 string, key2 string) bool {
    i := strings.Index(key2, "*")
    if i == -1 {
        return key1 == key2
    }

    if len(key1) > i {
        return key1[:i] == key2[:i]
    }
    return key1 == key2[:i]
}

初始化配置

初始化casbin可以参考官方示例:https://casbin.org/zh/docs/get-started

在此给出个人初始化方法

casbin.go

代码语言:go复制
package auth

import (
	"os"

	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	gormadapter "github.com/casbin/gorm-adapter/v3"
)

var Casbin *casbin.Enforcer

func InitCasbin() {
    // os.Getenv("MYSQL_DSN")可以参考gorm官方示例:
    //user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local
	a, err := gormadapter.NewAdapter("mysql", os.Getenv("MYSQL_DSN"), true)
	if err != nil {
		panic(err)
	}

	m, err := model.NewModelFromString(`
	[request_definition]
	r = sub, obj, act
	
	[policy_definition]
	p = sub, obj, act, eft
	
	[role_definition]
	g = _, _
	
	[policy_effect]
	e = some(where (p.eft == allow)) && !some(where (p.eft == deny))
	
	[matchers]
	m = g(r.sub, p.sub) && keyMatch(r.act, p.act) && keyMatch(r.obj, p.obj)
	`)
	if err != nil {
		panic(err)
	}

	e, err := casbin.NewEnforcer(m, a)
	if err != nil {
		panic(err)
	}
	Casbin = e

	Casbin.LoadPolicy()

    // 检查基础权限是否存在,请按需修改
	if ok, _ := Casbin.Enforce("common_admin", "admin/user", "POST"); !ok {
		initPolicy()
	}
}

初始化基础策略,在此给出个人写法,可以自行调整

initPolicy

代码语言:go复制
func initPolicy() {
	// add base policies
    // 注意,此处一定要填写allow或deny,否则会报错
	Casbin.AddPolicies(
		[][]string{
            //  写入对应权限,请自行调整
			// suspend user can't do anything
			{model.StatusSuspendUser, "*", "*", "deny"},
			// inactive user can't create
			{model.StatusInactiveUser, "user*", "*", "allow"},
			{model.StatusInactiveUser, "file*", "GET", "allow"},
			{model.StatusInactiveUser, "file*", "DELETE", "allow"},
			{model.StatusInactiveUser, "filefolder*", "GET", "allow"},
			{model.StatusInactiveUser, "filefolder*", "DELETE", "allow"},
			{model.StatusInactiveUser, "filestore*", "GET", "allow"},
			{model.StatusInactiveUser, "share*", "GET", "allow"},
			{model.StatusInactiveUser, "share*", "DELETE", "allow"},
			// active user can create file, filefolder and share
			{model.StatusActiveUser, "share*", "*", "allow"},
			{model.StatusActiveUser, "file*", "*", "allow"},
			{model.StatusActiveUser, "filefolder*", "*", "allow"},
			{model.StatusActiveUser, "rank*", "GET", "allow"},
			// admin user can change user status
			{model.StatusAdmin, "admin/user*", "*", "allow"},
			{model.StatusAdmin, "admin/login*", "*", "allow"},
			// super admin can do anything
			{model.StatusSuperAdmin, "*", "*", "allow"},
		},
	)
	// add group policies
	Casbin.AddGroupingPolicies(
		[][]string{
			{model.StatusActiveUser, model.StatusInactiveUser},
			{model.StatusAdmin, model.StatusActiveUser},
		},
	)
	Casbin.SavePolicy()
}

具体名称含义

代码语言:go复制
const (
	// super admin
	StatusSuperAdmin = "super_admin"
	// admin User
	StatusAdmin = "common_admin"
	// active User
	StatusActiveUser = "active"
	// inactive User
	StatusInactiveUser = "inactive"
	// Suspend User
	StatusSuspendUser = "suspend"
)

后续直接在启动服务的时候进行初始化即可

代码语言:go复制
func Init() {
    //其他初始化
    ............
	// start casbin
	auth.InitCasbin()
}

中间件写法

casbin已经配置好了,接下来就是编写中间件进行权限验证,在此给出gin中间件写法

代码语言:go复制
func CasbinAuth() gin.HandlerFunc {
	return func(c *gin.Context) {
        // 获取用户状态,此处已将状态写入gin.context
		userStatus := c.MustGet("Status").(string)
        // 获取用户申请的资源和方法
		method := c.Request.Method
		path := c.Request.URL.Path
        //过滤默认头部,这里将/api/v1/ 过滤
		object := strings.TrimPrefix(path, "/api/v1/")

        // 使用casbin提供的函数进行权限验证
		if ok, _ := auth.Casbin.Enforce(userStatus, object, method); !ok {
			// 如果无权限则返回无权限
            c.JSON(200, serializer.NotAuthErr("not auth"))
            // 中断后续执行
			c.Abort()
		}
	}
}

最后将中间件加入路由即可,在此给出个人使用gin框架写法

代码语言:go复制
v1 := r.Group("/api/v1")
	{
		auth := v1.Group("")
		auth.Use(middleware.CasbinAuth())
		{
            // 放入需要权限验证的api
		}
	}

一个需要注意的问题

Q:如果需要动态修改权限怎么办?

A:如果想要修改用户权限,直接修改存储的用户身份即可。

如果想要添加新的权限或删除权限请使用casbin的对应函数,在此给出go的官方api

代码语言:go复制
// e.AddPolicy(...)
// e.RemovePolicy(...)
// Save the policy back to DB.
e.SavePolicy()

不要在initPolicy进行修改,因为这样并不会将权限加入数据库。如果想要在initPolicy进行修改,请删除数据库中casbin自动创建的casbin_rule表。

结尾

如果有更多疑问,可以在评论区留言

0 人点赞