这篇博客仍是整理从https://github.com/LyricTian/gin-admin 这个项目中学习的golang相关知识。html
做者在项目中使用了 github.com/casbin/casbin
进行权限控制的,这个库本身以前也没有用过,正好能够经过这个项目学习一下使用。 固然这篇博客并不会对casbin的使用作很是详细的说明,感兴趣的能够去官网看具体的使用文档。git
ABAC: 基于属性的访问控制。github
DAC: 自主访问控制模型(DAC,Discretionary Access Control)是根据自主访问控制策略创建的一种模型,容许合法用户以用户或用户组的身份访问策略规定的客体,同时阻止非受权用户访问客体。拥有客体权限的用户,能够将该客体的权限分配给其余用户。golang
ACL: ACL是最先也是最基本的一种访问控制机制,它的原理很是简单:每一项资源,都配有一个列表,这个列表记录的就是哪些用户能够对这项资源执行CRUD中的那些操做。当系统试图访问这项资源时,会首先检查这个列表中是否有关于当前用户的访问权限,从而肯定当前用户能否执行相应的操做。总得来讲,ACL是一种面向资源的访问控制模型,它的机制是围绕“资源”展开的。web
RBAC: 基于角色的访问控制(RBAC, Role Based Access Control)在用户和权限之间引入了“角色(Role)”的概念,角色解耦了用户和权限之间的关系。数据库
casbin使用配置文件来设置访问控制模型。在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。app
Casbin中最基本、最简单的model是ACL。ACL中的model 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
Request definition: 表明请求,上面的配置中 r = sub, obj, act
表明一个请求有三个标准元素:请求主体,请求对象,请求操做。学习
Policy definition: 表明策略,表示具体的权限定义的规则是什么,上面配置中 p = sub, obj, act
code
Policy effect: Effect 用来判断若是一个请求知足了规则,是否须要赞成请求
Matchers: 有请求,有规则,那么请求是否匹配某个规则,则是matcher进行判断的
model的配置:
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "root"
Policey 配置:
p, alice, data1, read p, bob, data2, write
当咱们请求:alice,data1,read
根据匹配规则,匹配的结果就是true
当咱们请求:alice,data1,write
根据匹配规则,匹配的规则就是false
model的配置:
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [policy_effect] e = some(where (p.eft == allow)) [matchers] m = r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
Policy 定义:
p, alice, /alice_data/:resource, GET p, alice, /alice_data2/:id/using/:resId, GET
当咱们请求 alice, /alice_data/hello, GET
根据matchers规则匹配了 p, alice, /alice_data/:resource, GET
因此返回true
当咱们请求 alice, /alice_data/hello, POST
根据matchers规则没有匹配到,因此返回false
model的配置:
[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
在这里引入了role_definition
角色定义, g 用于判断哪一个用户是否属于哪一个角色
Policy 配置:
p, alice, data1, read p, bob, data2, write p, data2_admin, data2, read p, data2_admin, data2, write g, alice, data2_admin
当咱们请求 alice, data2, read
根据matchers 匹配了alice 是data2_admin角色,而且 r.obj == p.obj && r.act == p.act
因此返回true
这里先梳理一下做者代码中对casbin的使用,由于以前看了casbin在其余几个项目中的使用,感受都是有点乱,在在gin-admin这个项目的时候一开始也是感受有点懵 ,没有理解怎么用,不过当把代码梳理清楚以后,感受gin-admin做者的使用仍是很是好的。
gin-admin项目中关于casbin的使用分为
做者在gin-admin/internal/module/adapter/casbin.go 中定义了CasbinAdapter:
// CasbinAdapter casbin适配器 type CasbinAdapter struct { RoleModel model.IRole RoleMenuModel model.IRoleMenu MenuResourceModel model.IMenuActionResource UserModel model.IUser UserRoleModel model.IUserRole }
这里的CasbinAdapter是实现了casbin
中的Adapter
接口,即CasbinAdapter实现了LoadPolicy,SavePolicy,AddPolicy,RemovePolicy方法,而且做者经过在LoadPolicy将用户权限和角色权限从数据库中进行加载。
权限的初始化是经过下面代码:
func InitCasbin(adapter persist.Adapter) (*casbin.SyncedEnforcer, func(), error) { cfg := config.C.Casbin if cfg.Model == "" { return new(casbin.SyncedEnforcer), nil, nil } e, err := casbin.NewSyncedEnforcer(cfg.Model) if err != nil { return nil, nil, err } e.EnableLog(cfg.Debug) err = e.InitWithModelAndAdapter(e.GetModel(), adapter) if err != nil { return nil, nil, err } e.EnableEnforce(cfg.Enable) cleanFunc := func() {} if cfg.AutoLoad { e.StartAutoLoadPolicy(time.Duration(cfg.AutoLoadInternal) * time.Second) cleanFunc = func() { e.StopAutoLoadPolicy() } } return e, cleanFunc, nil }
这个部分主要是当咱们经过页面进行权限的配置后,咱们须要将权限从新进行加载,这部分代码在gin-admin/internal/app/bll/impl/bll/b_casbin.go中:
var chCasbinPolicy chan *chCasbinPolicyItem type chCasbinPolicyItem struct { ctx context.Context e *casbin.SyncedEnforcer } func init() { chCasbinPolicy = make(chan *chCasbinPolicyItem, 1) go func() { for item := range chCasbinPolicy { err := item.e.LoadPolicy() if err != nil { logger.Errorf(item.ctx, "The load casbin policy error: %s", err.Error()) } } }() } // LoadCasbinPolicy 异步加载casbin权限策略 func LoadCasbinPolicy(ctx context.Context, e *casbin.SyncedEnforcer) { if !config.C.Casbin.Enable { return } if len(chCasbinPolicy) > 0 { logger.Infof(ctx, "The load casbin policy is already in the wait queue") return } chCasbinPolicy <- &chCasbinPolicyItem{ ctx: ctx, e: e, } }
而在咱们的更改权限的接口中都会经过调用LoadCasbinPolicy将权限策略进行加载。
关于这个项目整理了三篇文章,也学习到了不少东西,其实到这篇文章,做者总体代码本身已经树立清楚了,不少人会以为做者的项目目录过于复杂,还有一些重复代码,在你刚开始梳理代码逻辑的时候还会感到一脸懵,可是当你耐心梳理完以后,你会发现,这样写原来会有这样或者那样的好处。做者剩余的代码就是关于web接口中的逻辑了,就不在作整理。
后面的计划是经过此次对此次代码的学习,写一个blog的web项目。同时也会找下一个开源项目代码进行学习