

集成go和casbin
source link: http://www.hi-roy.com/2020/09/28/集成gin和casbin/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

原文 ,省略了一些无关的内容。
如果你搜到这篇文章,那么什么是gin以及casbin应该不用过多解释了。
项目结构
root/ main.go # entry point of application handler/ # Gin handler functions middleware/ # Gin middlewares config/ # some configuration files like Casbin's rbac_model.conf component/ # global components like GORM DB instance
初始化数据库和缓存
在 component
目录下创建 persistence.go
用于初始化,这里使用 GORM
来处理数据库, BigCache
处理缓存:
import ( "fmt" "github.com/allegro/bigcache" _ "github.com/go-sql-driver/mysql" "github.com/jinzhu/gorm" "time" ) var ( DB *gorm.DB GlobalCache *bigcache.BigCache ) func init() { // Connect to DB var err error DB, err = gorm.Open("mysql", "your_db_url") if err != nil { panic(fmt.Sprintf("failed to connect to DB: %v", err)) } // Initialize cache GlobalCache, err = bigcache.NewBigCache(bigcache.DefaultConfig(30 * time.Minute)) // Set expire time to 30 mins if err != nil { panic(fmt.Sprintf("failed to initialize cahce: %v", err)) } }
在这个示例中,我们使用数据库来存储casbin的polices,使用缓存存储登录用户信息。
配置Casbin
Model Configuration File
首先,你也许会发现casbin中有些概念让你很困惑,比如 Model Configuration File
。这里我不想讨论太多原理(因为我也不熟),直接举个例子,使用基于角色的权限控制(RBAC,Role-based access control)。所以首先在 config
目录创建 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] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
上面的文件定义了casbin如何判断用户拥有什么权限,例子中我们定义了5个字段:
-
r = sub, obj, act
定义了一个请求需要由3部分组成:sub=用户,obj=URL或资源,act=操作。 -
p = sub, obj, act
定义了策略的格式,比如admin,dada,write
表示admin有data的写权限。 -
e = some(where (p.eft == allow))
定义了用户可以做那些策略中定义准许他做的事。 -
g = _, _
定义了角色的格式,例如bob,admin
表示用户bob是admin这个角色。 -
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
定义了鉴权时的流程,先检查用户角色,再检查用户访问的资源,最后检查用户行为。
上面几个部分,仅1、2、3、5是必须的,如果不使用RBAC可以忽略4。
Roy注:下面的这个更常用。
[matchers]
m = g(r.sub, p.sub) == true \
&& keyMatch2(r.obj, p.obj) == true \
&& regexMatch(r.act, p.act) == true \
|| r.sub == “admin” \
|| keyMatch2(r.obj, “/auth”) == true
Polices
举个例子:
p, user, data, read p, admin, data, read p, admin, data, write g, Alice, admin g, Bob, user
首先我们定义了3个策略:
- user可以读取data
- admin可以写data
- admin可以读data
以及2个用户角色:
- Alice属于admin
- Bob属于user
所以Alice有数据的所有权限而Bob只能读取数据。官网教程中casbin使用csv来简单的存储策略,这里我们使用数据库。casbin通常把表名命名为 casbin_rule
,结构语句如下:
CREATE TABLE casbin_rule ( p_type VARCHAR(100), v0 VARCHAR(100), v1 VARCHAR(100), v2 VARCHAR(100) ); INSERT INTO casbin_rule VALUES('p', 'user', 'data', 'read'); INSERT INTO casbin_rule(p_type, v0, v1) VALUES('g', 'Bob', 'user');
实现Gin的Handler
首先实现登录逻辑
// handler/user_handler.go func Login(c *gin.Context) { username, password := c.PostForm("username"), c.PostForm("password") // Authentication // blahblah... // Generate random session id u, err := uuid.NewRandom() if err != nil { log.Fatal(err) } sessionId := fmt.Sprintf("%s-%s", u.String(), username) // Store current subject in cache component.GlobalCache.Set(sessionId, []byte(username)) // Send cache key back to client in cookie c.SetCookie("current_subject", sessionId, 30*60, "/resource", "", false, true) c.JSON(200, component.RestResponse{Code: 1, Message:username + " logged in successfully"}) }
如果登录成功,我们存储用户(或者叫sub)信息到缓存中,这里不要忘记将sessionId写回cookie中。casbin只负责鉴权不负责认证,所以我们要自己实现认证逻辑。接下来实现读、写逻辑:
// handler/resource_handler.go func ReadResource(c *gin.Context) { // some stuff // blahblah... c.JSON(200, component.RestResponse{Code: 1, Message: "read resource successfully", Data: "resource"}) } func WriteResource(c *gin.Context) { // some stuff // blahblah... c.JSON(200, component.RestResponse{Code: 1, Message: "write resource successfully", Data: "resource"}) }
然后实现 main.go
:
// main.go var ( router *gin.Engine ) func init() { // Initialize gin router router = gin.Default() corsConfig := cors.DefaultConfig() corsConfig.AllowAllOrigins = true corsConfig.AllowCredentials = true router.Use(cors.New(corsConfig)) // CORS configuraion router.POST("/user/login", handler.Login) router.GET("/resource", handler.ReadResource) router.POST("/resource", handler.WriteResource) } func main() { defer component.DB.Close() // Start our application err := router.Run(":8081") if err != nil { panic(fmt.Sprintf("failed to start gin engin: %v", err)) } log.Println("application is now running...") }
一切就绪,接下来开始集成。
启用casbin策略
从数据库加载polices
第一个问题就是,我们如何从数据库动态加载策略?我们可以使用 Casbin Adapters
,更精确的说我们使用的是 Gorm Adapter
。首先进行初始化:
// main.go func init() { // Initialize casbin adapter adapter, err := gormadapter.NewAdapterByDB(component.DB) if err != nil { panic(fmt.Sprintf("failed to initialize casbin adapter: %v", err)) } // Initialize gin router router = gin.Default() corsConfig := cors.DefaultConfig() corsConfig.AllowAllOrigins = true corsConfig.AllowCredentials = true router.Use(cors.New(corsConfig)) // CORS configuraion router.POST("/user/login", handler.Login) router.GET("/resource", handler.ReadResource) router.POST("/resource", handler.WriteResource) }
显然的,在进行任何操作前都需要经过鉴权,所以更优雅的方式是使用gin提供的 middlewares
和 grouping routes
:
// middleware/access_control.go // Authorize determines if current subject has been authorized to take an action on an object. func Authorize(obj string, act string, adapter *gormadapter.Adapter) gin.HandlerFunc { return func(c *gin.Context) { // Get current user/subject val, existed := c.Get("current_subject") if !existed { c.AbortWithStatusJSON(401, component.RestResponse{Message: "user hasn't logged in yet"}) return } // Casbin enforces policy ok, err := enforce(val.(string), obj, act, adapter) if err != nil { log.Println(err) c.AbortWithStatusJSON(500, component.RestResponse{Message: "error occurred when authorizing user"}) return } if !ok { c.AbortWithStatusJSON(403, component.RestResponse{Message: "forbidden"}) return } c.Next() } } func enforce(sub string, obj string, act string, adapter *gormadapter.Adapter) (bool, error) { // Load model configuration file and policy store adapter enforcer, err := casbin.NewEnforcer("config/rbac_model.conf", adapter) if err != nil { return false, fmt.Errorf("failed to create casbin enforcer: %w", err) } // Load policies from DB dynamically err = enforcer.LoadPolicy() if err != nil { return false, fmt.Errorf("failed to load policy from DB: %w", err) } // Verify ok, err := enforcer.Enforce(sub, obj, act) return ok, err }
最后进行一些修改:
// main.go func init() { // Initialize casbin adapter adapter, err := gormadapter.NewAdapterByDB(component.DB) if err != nil { panic(fmt.Sprintf("failed to initialize casbin adapter: %v", err)) } // Initialize Gin router router = gin.Default() corsConfig := cors.DefaultConfig() corsConfig.AllowAllOrigins = true corsConfig.AllowCredentials = true router.Use(cors.New(corsConfig)) // CORS configuraion router.POST("/user/login", handler.Login) // Secure our API resource := router.Group("/api") { resource.GET("/resource", middleware.Authorize("resource", "read", adapter), handler.ReadResource) resource.POST("/resource", middleware.Authorize("resource", "write", adapter), handler.WriteResource) } }
大功告成。
Recommend
-
154
Basic Role-Based HTTP Authorization in Go with Casbin Authentication and Authorization are essential parts of any secured Web Application. I recently finished writing my first serious web application in Go and t...
-
173
Iris + Casbin 权限控制实战
-
119
README.md Gin Admin 基于 Gin + GORM + Casbin + Ant Design React 实现的RBAC权限管理脚手架,目的是提供一套轻量的中后台开发框架,方便、快速的完成业务...
-
61
简介 Casbin可以做到: 支持自定义请求的格式,默认的请求格式为{subject, object, action}。 具有访问控制模型model和策略policy两个核心概念。 支持RBAC中的多层角色继承,不止主体可以有...
-
24
简介 权限管理在几乎每个系统中都是必备的模块。如果项目开发每次都要实现一次权限管理,无疑会浪费开发时间,增加开发成本。因此, casbin 库出现了。 casbin 是一个强大、高效的访问控制库。支持...
-
17
集成gin和casbin9月 28, 2020 发布在 Golang, 菜鸟翻译...
-
20
How to use casbin authorization in your rust web-app [Part - 3] Jun 12 ・6 min read...
-
60
Casdoor A UI-first centralized authenticatio...
-
16
Authentication and authorization in Gin application with JWT and CasbinIntroductionJWT ConceptsJSON Web Token (JWT) is an open standard (RFC 7519) that de...
-
10
Casbin开源社区推出开源身份认证、单点登录框架Casdoor! douke0320 · 7天之前 · 385 次点击 · 预...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK