Casbin是一個強大的、高效的開源訪問控制架構,其權限管理機制支持多種訪問控制模型。
https://casbin.org/docs/zh-CN/overview
Casbin 可以:
l 支持自定義請求的格式,默認的請求格式為{subject, object, action}。
l 具有訪問控制模型model和策略policy兩個核心概念。
l 支持RBAC中的多層角色繼承,不止主體可以有角色,資源也可以具有角色。
l 支持超級用戶,如 root 或 Administrator,超級用戶可以不受授權策略的約束訪問任意資源。
l 支持多種內置的操作符,如 keyMatch,方便對路徑式的資源進行管理,如 /foo/bar 可以映射到 /foo*
Casbin 不能:
l 身份認證 authentication(即驗證用戶的用戶名、密碼),casbin只負責訪問控制。應該有其他專門的組件負責身份認證,然后由casbin進行訪問控制,二者是相互配合的關系。
l 管理用戶列表或角色列表。 Casbin 認為由項目自身來管理用戶、角色列表更為合適, 用戶通常有他們的密碼,但是Casbin的設計思想並不是把它作為一個存儲密碼的容器,而是存儲RBAC方案中用戶和角色之間的映射關系。
基礎
Casbin兩個核心概念是訪問控制模型model和訪問控制策略policy,分別對應兩個文件:model配置文件和policy策略文件。在 Casbin 中, 訪問控制模型model被抽象為基於 PERM (Policy策略, Effect效果, Request請求, Matchers匹配器) 的一個文件,因此切換或升級項目的授權機制與修改配置一樣簡單。可以通過組合可用的模型來定制訪問控制模型。
Model和policy模板可參考:https://casbin.org/docs/zh-CN/supported-models,支持ACL,RBAC,ABAC等。
{subject, object, action}:主體subject表示用戶角色,object客體表示訪問路徑(資源),action表示請求方法。
匹配器定義了策略是如何匹配的,可以通過直接定義實體,或者使用keyMatch方法,也可以匹配通配符。
[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act // 定義sub可以執行obj資源的act P2 = sub, act //定義sub所有的資源都能執行act [role_definition] // 基於RBAC時需要,定義role g = _, _ //用戶和角色 g2 = _, _, //用戶、角色、域 [policy_effect] e = some(where (p.eft == allow)) // 有任意一條policy rule滿足,則最終結果為allow [matchers] m = r.sub == p.sub && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*") //定義request和policy匹配的方式,p.eft是allow還是deny基於此來決定
在安全性方面,通常會選擇最簡單的解決方案,因為當系統開始變復雜和難以維護時,錯誤就開始發生。
p, admin, /*, * p, anonymous, /login, * p, member, /logout, * p, member, /member/*, *
Policy配置admin 角色可以訪問所有內容,member 角色可以訪問以 /member/ 開頭的路徑和 logout 路徑,未認證用戶可以登陸。
存儲
官網上介紹model只能加載,不能保存。因為作者認為 model 不是動態組件,不應該在運行時進行修改,所以我們沒有實現一個 API 來將 model 保存到存儲中。提供了三種等效的方法來靜態或動態地加載模型:
.CONF文件,代碼,字符串
Policy策略存儲作為adapter實現(Casbin的中間件) 實現。Casbin用戶可以使用adapter從存儲中加載策略規則 (aka LoadPolicy()) 或者將策略規則保存到其中 (aka SavePolicy())。 為了保持代碼輕量級,沒有把adapter代碼放在主庫中。可參考:https://github.com/HaoweiCh/go-casbin-http-authrozation (mysql policy)
角色管理器用於管理Casbin中的RBAC角色層次結構(用戶角色映射)。 角色管理器可以從Casbin策略規則或外部源(如LDAP、Okta、Auth0、Azure AD等) 檢索角色數據。 我們支持角色管理器的不同實現。 為了保持代碼輕量級,我們沒有把角色管理器代碼放在主庫中(默認的角色管理器除外)。
應用
通常casbin要與身份認證(authorization)如session管理或jwt配合使用。
gin-casbin插件插件使用參考:https://github.com/maxwellhertz/gin-casbin.git
訪問資源時需要鑒權中間件判斷用戶合法性,並分配合適的角色。初步鑒權成功后,將用戶角色,請求路徑和請求方法傳給casbin執行器,執行器根據model和policy確認該角色(subject)的用戶是否允許訪問該請求方法(action)和路徑(object)指定的資源。若校驗失敗,應返回403,若通過,則繼續執行后續處理函數。
參考:在 Go 語言中使用 casbin 實現基於角色的 HTTP 權限控制
一般應用就是兩個函數:
1. New a Casbin enforcer with a model file and a policy file: e, _ := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv") Note: you can also initialize an enforcer with policy in DB instead of file.
2. Add an enforcement hook into your code right before the access happens: sub := "alice" // the user that wants to access a resource. obj := "data1" // the resource that is going to be accessed. act := "read" // the operation that the user performs on the resource. if res := e.Enforce(sub, obj, act); res { // permit alice to read data1 } else { // deny the request, show an error } 3. Besides the static policy file, Casbin also provides API for permission management at run-time. For example, You can get all the roles assigned to a user as below: roles, _ := e.GetImplicitRolesForUser(sub)
a := mysqladapter.NewDBAdapter("mysql", "mysql_username:mysql_password@tcp(127.0.0.1:3306)/") e := casbin.NewEnforcer("path/to/basic_model.conf", a)
_ = e.LoadPolicy()
數據庫結構如下:
mysql> select * from casbin_rule; +--------+------+---------------------------------------------------+--------+------+------+------+ | p_type | v0 | v1 | v2 | v3 | v4 | v5 | +--------+------+---------------------------------------------------+--------+------+------+------+ | p | 8881 | /base/login | POST | | | | | p | 8881 | /base/register | POST | | | | | p | 8881 | /api/createApi | POST | | | |
示例:
package main import ( "errors" "fmt" "log" "net/http" "time" "github.com/casbin/casbin" "github.com/alexedwards/scs/v2" "testcasbin/auth" "testcasbin/model" ) var sessionManager *scs.SessionManager func main() { fmt.Println("hello world") authEnforcer, err := casbin.NewEnforcerSafe("model.conf", "policy.csv") if err != nil { log.Fatal(err) } sessionManager = scs.New() sessionManager.Lifetime = 2 * time.Minute users := createUsers() mux := http.NewServeMux() mux.HandleFunc("/login", loginHandler(users)) mux.HandleFunc("/logout", logoutHandler()) mux.HandleFunc("/member/id", memberHandler()) mux.HandleFunc("/member/role", memberRoleHandler()) mux.HandleFunc("/admin/stuff", adminHandler()) log.Print("Server started on localhost:8088") log.Fatal(http.ListenAndServe(":8088", sessionManager.LoadAndSave(auth.Authorizer(authEnforcer, users, sessionManager)(mux)))) } ... package auth import ( "errors" "log" "net/http" "testcasbin/model" "github.com/alexedwards/scs/v2" "github.com/casbin/casbin" ) func Authorizer(e *casbin.Enforcer, users model.Users, session *scs.SessionManager) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { var role string var uid int role = session.GetString(r.Context(), "role") if role == "" { role = "anonymous" } if role == "member" { uid = session.GetInt(r.Context(), "userID") if uid == 0 { writeError(http.StatusInternalServerError, "ERROR_NOT_GET_UID", w, errors.New("userID is not in session")) return } exists := users.Exists(uid) if !exists { writeError(http.StatusForbidden, "FORBIDDEN", w, errors.New("user does not exist")) return } } log.Printf("role: %v, uid: %v, path: %v, method: %v\n", role, uid, r.URL.Path, r.Method) res, err := e.EnforceSafe(role, r.URL.Path, r.Method) if err != nil { writeError(http.StatusInternalServerError, "ERROR_CASBIN", w, err) return } if res { next.ServeHTTP(w, r) } else { writeError(http.StatusForbidden, "FORBIDDEN", w, errors.New("unauthorized")) return } } return http.HandlerFunc(fn) } } func writeError(status int, message string, w http.ResponseWriter, err error) { log.Print("ERROR: ", err.Error()) w.WriteHeader(status) w.Write([]byte(message)) }
參考:
1. https://casbin.org/docs/zh-CN/overview 中文文檔
2. 在 Go 語言中使用 casbin 實現基於角色的 HTTP 權限控制 基於session身份認證
https://zupzup.org/casbin-http-role-auth/ 英文
3. https://github.com/HaoweiCh/go-casbin-http-authrozation
簡單且實用的 HTTP 鑒權體系 (mysql policy + redis session)
4. https://github.com/maxwellhertz/gin-casbin.git gin插件和示例(基於jwt和session)
5. Casbin權限模型 csdn