casbin + gin + gorm

发布时间 2023-06-20 17:44:20作者: 北方姆Q

实际项目中肯定要用rbac这种权限模型,因此model使用如下

rbac_model.conf
[request_definition]
r = sub, obj, act

[policy_definition]
p = sub, obj, act

[role_definition]
g = _, _

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

[matchers]
# 当访问实体为root时直接认证通过,项目肯定需要特殊权限用户
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act || r.sub == "root"

由于casbin、gorm都有不同版本,因此引用的包一定要对上!!!

casbin.go
import (
	"github.com/casbin/casbin/v2"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

const (
	// 刚才的rbac配置文件路径
	configName = "./configs/rbac_model.conf"
)

var (
	enforcer *casbin.Enforcer

	errErrorPermission = errors.New("权限有误")
)

func InitCasbin() {
	// 这里填入自己的"gorm.io/gorm"的db链接,而不是jinzhu/gorm的,会对不上
	adapter, err := gormadapter.NewAdapterByDB(*gorm.DB)
	if err != nil {
		panic(err)
	}
	enforcer, err = casbin.NewEnforcer(configName, adapter)
	if err != nil {
		panic(err)
	}
	enforcer.EnableLog(false)
	err = enforcer.LoadPolicy()
	if err != nil {
		panic(err)
	}
}

casbin启动默认会在数据库下新建一张casbin_rule表,如果创建有问题,可能是默认引擎是MyISAM,而需要用InnoDB,可以显示的AutoMigrate

db.Set("gorm:table_options", "ENGINE=InnoDB;CHARSET=utf8mb4").AutoMigrate(&gormadapter.CasbinRule{})

在casbin_rule表中可以定义某些用户加入组,然后以“组”概念去匹配权限,但是这样你的user_message表跟user_role表等于直接用casbin_rule里的定义了,或者需要做关联关系,后面新增用户加权限等等是在操作casbin_rule表的。实际情况下不合理,因此还是自己定义好user_message表跟user_role表,并维护好这两个表多对多关系即可,而casbin的实体验证直接验证“权限”这个概念即可,因此casbin_rule表仅需要保留Policie信息即可

casbin_rule表
# 注意get要小写!
INSERT INTO `casbin_rule` (`ptype`,`v0`,`v1`,`v2`) VALUES ('p','dev','/api/dev/*','get');
casbin.go
func CasbinMiddleWare() func(c *gin.Context) {
	return func(c *gin.Context) {
		// 白名单下url不验证
		for _, whiteUrl := range whiteList {
			if strings.Contains(c.Request.URL.String(), whiteUrl) {
				c.Next()
				return
			}
		}

		username := c.GetString("username")
		// 换成自己的角色权限字段
		iuserRoles, exists := c.Get("userRoles")
		zap.L().Debug("c.Get", zap.String("username", username), zap.Any("iuserRoles", iuserRoles))
		if username == "" || !exists {
			handlerbase.BuildErrorNilResponse(c, errErrorAuth)
			c.Abort()
			return
		}

		p := c.Request.URL.Path
		m := c.Request.Method
		zap.L().Debug("Request", zap.String("p", p), zap.String("m", m))
		userRoles := iuserRoles.([]model.UserRole)
		for _, userRole := range userRoles {
			// 如果不使用strings.ToLower则casbin_rule表中act需要为大写!
			// act实体验证该权限名称!
			hit, err := enforcer.Enforce(userRole.RoleName, p, strings.ToLower(m))
			if err != nil || !hit {
				continue
			} else {
				c.Next()
				return
			}
		}

		handlerbase.BuildErrorNilResponse(c, errErrorPermission)
		c.Abort()
	}
}