這篇博客仍是整理從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項目。同時也會找下一個開源項目代碼進行學習