Casbin + Gin + Gorm 学习探索

Casbin 是一个强大的,开源的访问控制框架,权限管理机制支持多种访问控制模型; 并且支持多种编程语言; 

 文档地址:https://casbin.org/docs/zh-CN/overview

Gin Golang 的 Web 框架,短小精悍

 文档地址: https://gin-gonic.com/docs/

Gorm Golang 的 ORM 框架

 文档地址:http://gorm.book.jasperxu.com/

今天我们要学习的是如何通过Casbin 来控制开发的 API 访问权限 

##### Casbin 工作原理

  访问控制模型被抽象为PERM(Policy,Effect,Request,Matcher) 的一个文件,如果切换项目的授权机制只用修改文件即可; 

  Policy: 定义权限的规则 

  Effect: 定义组合多个Policy 后是允许还是拒绝(allow/deny)

  Request: 访问的请求,可以理解为谁想访问什么资源

  Matcher:判断Request 是否满足Policy ,返回true或false 

##### 在使用Casbin 控制后台接口时使用以下模型

[request_definition]
    r = sub, obj, act
# 请求的规则 
# r 是规则的名称,sub 为请求的实体,obj 为资源的名称, act 为请求的实际操作动作 [policy_definition] p
= sub, obj, act # 策略的规则
# 同请求 [role_definition] g
= _, _ # 角色的定义
# g 角色的名称,第一个位置为用户,第二个位置为角色,第三个位置为域(在多租户场景下使用) [policy_effect] e
= some(where (p.eft == allow)) # 任意一条 policy rule 满足, 则最终结果为 allow [matchers] m = g(r.sub, p.sub) == true \ && keyMatch2(r.obj, p.obj) == true \ && regexMatch(r.act, p.act) == true \ || r.sub == "root"
# 前三个用来匹配上面定义的请求的规则, 最后一个或条件为:如果实体是root 直接通过, 不验证权限

在理解了Casbin 的工作原理后,实际写代码测试一下

需要使用的外部包

go get github.com/casbin/casbin  Casbin 官方库

go get github.com/casbin/gorm-adapter  Casbin 插件,用来将规则和策略保存到数据库中

go get github.com/gin-gonic/gin  Go Web 框架

go get github.com/go-sql-driver/mysql  Go MySQL 驱动 

go get github.com/jinzhu/gorm  Go ORMpackage mainimport (    "fmt"    "github.com/casbin/casbin"

    "github.com/casbin/gorm-adapter"
    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql"
    "github.com/jinzhu/gorm"
)

// 统一响应结构体 type Response
struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data"` } var O *gorm.DB var PO *gormadapter.Adapter var Enforcer *casbin.Enforcer
func ping(c
*gin.Context) { var response Response response.Code = 0 response.Message = "success" response.Data = "" c.JSON(200, response) return } // 数据库连接及角色规则的初始化 func connect() { dsn := "xxx:xxx@(xxxx:3xx6)/goapp?charset=utf8&parseTime=True&loc=Local" var err error O, err = gorm.Open("mysql", dsn) if err != nil { fmt.Println("connect DB error") panic(err) }
   // 将数据库连接同步给插件, 插件用来操作数据库 PO
= gormadapter.NewAdapterByDB(O)
// 这里也可以使用原生字符串方式
// Enforcer
= casbin.NewEnforcer("./auth_model.conf", PO)
// 开启权限认证日志 Enforcer.EnableLog(
true)
// 加载数据库中的策略 err
= Enforcer.LoadPolicy() if err != nil { fmt.Println("loadPolicy error") panic(err) } // 创建一个角色,并赋于权限
// admin 这个角色可以访问GET 方式访问 /api/v2/ping res := Enforcer.AddPolicy("admin","/api/v2/ping","GET") if !res { fmt.Println("policy is exist") } else { fmt.Println("policy is not exist, adding") } // 将 test 用户加入一个角色中 Enforcer.AddRoleForUser("test","root")
Enforcer.AddRoleForUser("tom","admin")
// 请看规则中如果用户名为 root 则不受限制 } func main() { defer O.Close() connect() g := gin.Default()
  // 这里的接口没有使用权限认证中间件 version1 :
= g.Group("/api/v1") { version1.GET("/ping", ping) // 这个是通用的接口 }
// 接口使用权限认证中间件 version2 :
= g.Group("/api/v2", CasbinMiddleWare) { version2.GET("/ping", ping) } _ = g.Run(":8099") } // casbin middleware 权限认证中间件 func CasbinMiddleWare(c *gin.Context) { var userName string userName = c.GetHeader("userName") if userName == "" { fmt.Println("headers invalid") c.JSON(200, gin.H{ "code": 401, "message": "Unauthorized", "data": "", }) c.Abort() return } // 请求的path p := c.Request.URL.Path // 请求的方法 m := c.Request.Method
// 这里认证 res,err := Enforcer.EnforceSafe(userName,p,m) // 这个 HasPermissionForUser 跟上面的有什么区别
// EnforceSafe 会验证角色的相关的权限
// 而 HasPermissionForUser 只验证用户是否有权限
//res = Enforcer.HasPermissionForUser(userName,p,m) if err != nil { fmt.Println("no permission ") fmt.Println(err) c.JSON(200, gin.H{ "code": 401, "message": "Unauthorized", "data": "", }) c.Abort() return } if !res { fmt.Println("permission check failed") c.JSON(200, gin.H{ "code": 401, "message": "Unauthorized", "data": "", }) c.Abort() return } c.Next() }

 

程序运行后数据库数据为

 

 

 

 

 p 代表的是策略 admin 角色可以使用GET 访问 /api/v2/ping 

 g 代表的是角色 test 用户在root 角色中

                           tom 在admin 角色中

所以在测试时请求头

userName = root 有正常响应 (这里不会到数据库验证,策略最后一条)

userName = tom 正常响应 (tom 有admin 角色 , 所以验证通过)

userName = role_admin 正常响应 (参考这里:https://casbin.org/docs/zh-CN/rbac , 正常情况下用户名和角色名称不应该一样)

userName = *** 都无法通过认证

########

   最后, 一般的后台开发不会这样设计, 后面继续学习,理解 Casbin 工作原理,为以后开发打下基础; 

posted @ 2019-11-28 17:27  mail_maomao  阅读(5148)  评论(1编辑  收藏  举报