casbin應用


Casbin是一個強大的、高效的開源訪問控制架構,其權限管理機制支持多種訪問控制模型。

官網:https://casbin.org/, 

https://github.com/casbin

https://casbin.org/docs/zh-CN/overview

Casbin 可以:

支持自定義請求的格式,默認的請求格式為{subject, object, action}

具有訪問控制模型model和策略policy兩個核心概念。

支持RBAC中的多層角色繼承,不止主體可以有角色,資源也可以具有角色。

支持超級用戶,如 root  Administrator,超級用戶可以不受授權策略的約束訪問任意資源。

支持多種內置的操作符,如 keyMatch,方便對路徑式的資源進行管理,如 /foo/bar 可以映射到 /foo*

Casbin 不能:

身份認證 authentication(即驗證用戶的用戶名、密碼)casbin只負責訪問控制。應該有其他專門的組件負責身份認證,然后由casbin進行訪問控制,二者是相互配合的關系。

管理用戶列表或角色列表 Casbin 認為由項目自身來管理用戶、角色列表更為合適, 用戶通常有他們的密碼,但是Casbin的設計思想並不是把它作為一個存儲密碼的容器而是存儲RBAC方案中用戶和角色之間的映射關系。

基礎

Casbin兩個核心概念是訪問控制模型model和訪問控制策略policy,分別對應兩個文件:model配置文件和policy策略文件。在 Casbin , 訪問控制模型model被抽象為基於 PERM (Policy策略, Effect效果, Request請求, Matchers匹配器的一個文件因此切換或升級項目的授權機制與修改配置一樣簡單。可以通過組合可用的模型來定制訪問控制模型。

Modelpolicy模板可參考: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策略規則或外部源(LDAPOktaAuth0Azure AD) 檢索角色數據。 我們支持角色管理器的不同實現。 為了保持代碼輕量級,我們沒有把角色管理器代碼放在主庫中(默認的角色管理器除外) 

應用

通常casbin要與身份認證(authorization)如session管理或jwt配合使用。

gin-casbin插件插件使用參考:https://github.com/maxwellhertz/gin-casbin.git

訪問資源時需要鑒權中間件判斷用戶合法性,並分配合適的角色。初步鑒權成功后,將用戶角色,請求路徑和請求方法傳給casbin執行器,執行器根據modelpolicy確認該角色(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插件和示例(基於jwtsession

5. Casbin權限模型 csdn

6. 多租戶通用權限設計(基於 casbin)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM