1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        GRBAC-storyicon基于角色的訪問控制框架

        聯(lián)合創(chuàng)作 · 2023-09-30 08:46

        Grbac是一個(gè)快速,優(yōu)雅和簡潔的RBAC(基于角色的訪問控制)框架。它支持增強(qiáng)的通配符并使用Radix樹匹配HTTP請求??梢栽谌魏维F(xiàn)有的數(shù)據(jù)庫和數(shù)據(jù)結(jié)構(gòu)中輕松使用它。

        grbac的作用是確保指定的資源只能由指定的角色訪問。請注意,grbac不負(fù)責(zé)存儲鑒權(quán)規(guī)則和分辨“當(dāng)前請求發(fā)起者具有哪些角色”,更不負(fù)責(zé)角色的創(chuàng)建、分配等。這意味著您應(yīng)該首先配置規(guī)則信息,并提供每個(gè)請求的發(fā)起者具有的角色。

        grbac將HostPathMethod的組合視為Resource,并將Resource綁定到一組角色規(guī)則(稱為Permission)。只有符合這些規(guī)則的用戶才能訪問相應(yīng)的Resource

        讀取鑒權(quán)規(guī)則的組件稱為Loader。grbac預(yù)置了一些Loader,你也可以通過實(shí)現(xiàn)func()(grbac.Rules,error)來根據(jù)你的設(shè)計(jì)來自定義Loader,并通過grbac.WithLoader加載它。

        1. 最常見的用例

        下面是最常見的用例,它使用gin,并將grbac包裝成了一個(gè)中間件。通過這個(gè)例子,你可以很容易地知道如何在其他http框架中使用grbac(比如echo,iris,ace等):

        package main
        
        import (
            "github.com/gin-gonic/gin"
            "github.com/storyicon/grbac"
            "net/http"
            "time"
        )
        
        func LoadAuthorizationRules() (rules grbac.Rules, err error) {
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 你可以從數(shù)據(jù)庫或文件加載授權(quán)規(guī)則
            // 但是你需要以 grbac.Rules 的格式返回你的身份驗(yàn)證規(guī)則
            // 提示:你還可以將此函數(shù)綁定到golang結(jié)構(gòu)體
            return
        }
        
        func QueryRolesByHeaders(header http.Header) (roles []string,err error){
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 這個(gè)邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
            return roles, err
        }
        
        func Authorization() gin.HandlerFunc {
            // 在這里,我們通過“grbac.WithLoader”接口使用自定義Loader功能
            // 并指定應(yīng)每分鐘調(diào)用一次LoadAuthorizationRules函數(shù)以獲取最新的身份驗(yàn)證規(guī)則。
            // Grbac還提供一些現(xiàn)成的Loader:
            // grbac.WithYAML
            // grbac.WithRules
            // grbac.WithJSON
            // ...
            rbac, err := grbac.New(grbac.WithLoader(LoadAuthorizationRules, time.Minute))
            if err != nil {
                panic(err)
            }
            return func(c *gin.Context) {
                roles, err := QueryRolesByHeaders(c.Request.Header)
                if err != nil {
                    c.AbortWithError(http.StatusInternalServerError, err)
                    return
                }
                state, _ := rbac.IsRequestGranted(c.Request, roles)
                if !state.IsGranted() {
                    c.AbortWithStatus(http.StatusUnauthorized)
                    return
                }
            }
        }
        
        func main(){
            c := gin.New()
            c.Use(Authorization())
        
            // 在這里通過c.Get、c.Post等函數(shù)綁定你的API
            // ...
        
            c.Run(":8080")
        }

        2. 概念

        這里有一些關(guān)于grbac的概念。這很簡單,你可能只需要三分鐘就能理解。

        2.1. Rule

        // Rule即規(guī)則,用于定義Resource和Permission之間的關(guān)系
        type Rule struct {
            // ID決定了Rule的優(yōu)先級。
            // ID值越大意味著Rule的優(yōu)先級越高。
            // 當(dāng)請求被多個(gè)規(guī)則同時(shí)匹配時(shí),grbac將僅使用具有最高ID值的規(guī)則。
            // 如果有多個(gè)規(guī)則同時(shí)具有最大的ID,則將隨機(jī)使用其中一個(gè)規(guī)則。
            ID int `json:"id"`
            *Resource
            *Permission
        }

        如你所見,Rule由三部分組成:ID,ResourcePermission。 “ID”確定規(guī)則的優(yōu)先級。 當(dāng)請求同時(shí)滿足多個(gè)規(guī)則時(shí)(例如在通配符中), grbac將選擇具有最高ID的那個(gè),然后使用其權(quán)限定義進(jìn)行身份驗(yàn)證。 如果有多個(gè)規(guī)則同時(shí)具有最大的ID,則將隨機(jī)使用其中一個(gè)規(guī)則(所以請避免這種情況)。

        下面有一個(gè)非常簡單的例子:

        #Rule
        - id: 0
          # Resource
          host: "*"
          path: "**"
          method: "*"
          # Permission
          authorized_roles:
          - "*"
          forbidden_roles: []
          allow_anyone: false
        
        #Rule 
        - id: 1
          # Resource
          host: domain.com
          path: "/article"
          method: "{DELETE,POST,PUT}"
          # Permission
          authorized_roles:
          - editor
          forbidden_roles: []
          allow_anyone: false

        在以yaml格式編寫的此配置文件中,ID=0 的規(guī)則表明任何具有任何角色的人都可以訪問所有資源。 但是ID=1的規(guī)則表明只有editor可以對文章進(jìn)行增刪改操作。 這樣,除了文章的操作只能由editor訪問之外,任何具有任何角色的人都可以訪問所有其他資源。

        2.2. Resource

        type Resource struct {
            // Host 定義資源的Host,允許使用增強(qiáng)的通配符。
            Host string `json:"host"`
            // Path 定義資源的Path,允許使用增強(qiáng)的通配符。
            Path string `json:"path"`
            // Method 定義資源的Method,允許使用增強(qiáng)的通配符。
            Method string `json:"method"`
        }

        Resource用于描述Rule適用的資源。 當(dāng)執(zhí)行IsRequestGranted(c.Request,roles)時(shí),grbac首先將當(dāng)前的Request與所有Rule中的Resources匹配。

        Resource的每個(gè)字段都支持增強(qiáng)的通配符

        2.3. Permission

        // Permission用于定義權(quán)限控制信息
        type Permission struct {
            // AuthorizedRoles定義允許訪問資源的角色
            // 支持的類型: 非空字符串,*
            //      *: 意味著任何角色,但訪問者應(yīng)該至少有一個(gè)角色,
            //      非空字符串:指定的角色
            AuthorizedRoles []string `json:"authorized_roles"`
            // ForbiddenRoles 定義不允許訪問指定資源的角色
            // ForbiddenRoles 優(yōu)先級高于AuthorizedRoles
            // 支持的類型:非空字符串,*
            //      *: 意味著任何角色,但訪問者應(yīng)該至少有一個(gè)角色,
            //      非空字符串:指定的角色
            //
            ForbiddenRoles []string `json:"forbidden_roles"`
            // AllowAnyone的優(yōu)先級高于 ForbiddenRoles、AuthorizedRoles
            // 如果設(shè)置為true,任何人都可以通過驗(yàn)證。
            // 請注意,這將包括“沒有角色的人”。
            AllowAnyone bool `json:"allow_anyone"`
        }

        “Permission”用于定義綁定到的“Resource”的授權(quán)規(guī)則。 這是易于理解的,當(dāng)請求者的角色符合“Permission”的定義時(shí),他將被允許訪問Resource,否則他將被拒絕訪問。

        為了加快驗(yàn)證的速度,Permission中的字段不支持“增強(qiáng)的通配符”。 在AuthorizedRolesForbiddenRoles中只允許*表示所有。

        2.4. Loader

        Loader用于加載Rule。 grbac預(yù)置了一些加載器,你也可以通過實(shí)現(xiàn)func()(grbac.Rules, error) 來自定義加載器并通過 grbac.WithLoader 加載它。

        method description
        WithJSON(path, interval) 定期從json文件加載規(guī)則配置
        WithYaml(path, interval) 定期從yaml文件加載規(guī)則配置
        WithRules(Rules) grbac.Rules加載規(guī)則配置
        WithAdvancedRules(loader.AdvancedRules) 以一種更緊湊的方式定義Rule,并使用loader.AdvancedRules加載
        WithLoader(loader func()(Rules, error), interval) 使用自定義函數(shù)定期加載規(guī)則

        interval定義了Rules的重載周期。 當(dāng)interval <0時(shí),grbac會放棄周期加載Rules配置; 當(dāng)interval∈[0,1s)時(shí),grbac會自動將interval設(shè)置為5s;

        3. 其他例子

        這里有一些簡單的例子,可以讓你更容易理解grbac的工作原理。 雖然grbac在大多數(shù)http框架中運(yùn)行良好,但很抱歉我現(xiàn)在只使用gin,所以如果下面的例子中有一些缺陷,請告訴我。

        3.1. gin && grbac.WithJSON

        如果你想在JSON文件中編寫配置文件,你可以通過grbac.WithJSON(filepath,interval)加載它,filepath是你的json文件路徑,并且grbac將每隔interval重新加載一次文件。 。

        [
            {
                "id": 0,
                "host": "*",
                "path": "**",
                "method": "*",
                "authorized_roles": [
                    "*"
                ],
                "forbidden_roles": [
                    "black_user"
                ],
                "allow_anyone": false
            },
            {
                "id":1,
                "host": "domain.com",
                "path": "/article",
                "method": "{DELETE,POST,PUT}",
                "authorized_roles": ["editor"],
                "forbidden_roles": [],
                "allow_anyone": false
            }
        ]

        以上是“JSON”格式的身份驗(yàn)證規(guī)則示例。它的結(jié)構(gòu)基于grbac.Rules

        func QueryRolesByHeaders(header http.Header) (roles []string,err error){
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 這個(gè)邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
            return roles, err
        }
        
        func Authentication() gin.HandlerFunc {
            rbac, err := grbac.New(grbac.WithJSON("config.json", time.Minute * 10))
            if err != nil {
                panic(err)
            }
            return func(c *gin.Context) {
                roles, err := QueryRolesByHeaders(c.Request.Header)
                if err != nil {
                    c.AbortWithError(http.StatusInternalServerError, err)
                    return
                }
        
                state, err := rbac.IsRequestGranted(c.Request, roles)
                if err != nil {
                    c.AbortWithStatus(http.StatusInternalServerError)
                    return
                }
        
                if !state.IsGranted() {
                    c.AbortWithStatus(http.StatusUnauthorized)
                    return
                }
            }
        }
        
        func main(){
            c := gin.New()
            c.Use(Authentication())
        
            // 在這里通過c.Get、c.Post等函數(shù)綁定你的API
            // ...
            
            c.Run(":8080")
        }
        

        3.2. echo && grbac.WithYaml

        如果你想在YAML文件中編寫配置文件,你可以通過grbac.WithYAML(file,interval)加載它,file是你的yaml文件路徑,并且grbac將每隔一個(gè)interval重新加載一次文件。

        #Rule
        - id: 0
          # Resource
          host: "*"
          path: "**"
          method: "*"
          # Permission
          authorized_roles:
          - "*"
          forbidden_roles: []
          allow_anyone: false
        
        #Rule 
        - id: 1
          # Resource
          host: domain.com
          path: "/article"
          method: "{DELETE,POST,PUT}"
          # Permission
          authorized_roles:
          - editor
          forbidden_roles: []
          allow_anyone: false

        以上是“YAML”格式的認(rèn)證規(guī)則的示例。它的結(jié)構(gòu)基于grbac.Rules。

        func QueryRolesByHeaders(header http.Header) (roles []string,err error){
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 這個(gè)邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
            return roles, err
        }
        
        func Authentication() echo.MiddlewareFunc {
            rbac, err := grbac.New(grbac.WithYAML("config.yaml", time.Minute * 10))
            if err != nil {
                    panic(err)
            }
            return func(echo.HandlerFunc) echo.HandlerFunc {
                return func(c echo.Context) error {
                    roles, err := QueryRolesByHeaders(c.Request().Header)
                    if err != nil {
                            c.NoContent(http.StatusInternalServerError)
                            return nil
                    }
                    state, err := rbac.IsRequestGranted(c.Request(), roles)
                    if err != nil {
                            c.NoContent(http.StatusInternalServerError)
                            return nil
                    }
                    if state.IsGranted() {
                            return nil
                    }
                    c.NoContent(http.StatusUnauthorized)
                    return nil
                }
            }
        }
        
        func main(){
            c := echo.New()
            c.Use(Authentication())
        
            // 在這里通過c.Get、c.Post等函數(shù)綁定你的API
            // ...
            
        }

        3.3. iris && grbac.WithRules

        如果你想直接在代碼中編寫認(rèn)證規(guī)則,grbac.WithRules(rules)提供了這種方式,你可以像這樣使用它:

        func QueryRolesByHeaders(header http.Header) (roles []string,err error){
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 這個(gè)邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
            return roles, err
        }
        
        func Authentication() iris.Handler {
            var rules = grbac.Rules{
                {
                    ID: 0,
                    Resource: &grbac.Resource{
                        Host: "*",
                        Path: "**",
                        Method: "*",
                    },
                    Permission: &grbac.Permission{
                        AuthorizedRoles: []string{"*"},
                        ForbiddenRoles: []string{"black_user"},
                        AllowAnyone: false,
                    },
                },
                {
                    ID: 1,
                    Resource: &grbac.Resource{
                        Host: "domain.com",
                        Path: "/article",
                        Method: "{DELETE,POST,PUT}",
                    },
                    Permission: &grbac.Permission{
                        AuthorizedRoles: []string{"editor"},
                        ForbiddenRoles: []string{},
                        AllowAnyone: false,
                    },
                },
            }
            rbac, err := grbac.New(grbac.WithRules(rules))
            if err != nil {
                panic(err)
            }
            return func(c context.Context) {
                roles, err := QueryRolesByHeaders(c.Request().Header)
                if err != nil {
                    c.StatusCode(http.StatusInternalServerError)
                    c.StopExecution()
                    return
                }
                state, err := rbac.IsRequestGranted(c.Request(), roles)
                if err != nil {
                    c.StatusCode(http.StatusInternalServerError)
                    c.StopExecution()
                    return
                }
                if !state.IsGranted() {
                    c.StatusCode(http.StatusUnauthorized)
                    c.StopExecution()
                    return
                }
            }
        }
        
        func main(){
            c := iris.New()
            c.Use(Authentication())
        
            // 在這里通過c.Get、c.Post等函數(shù)綁定你的API
            // ...
            
        }

        3.4. ace && grbac.WithAdvancedRules

        如果你想直接在代碼中編寫認(rèn)證規(guī)則,grbac.WithAdvancedRules(rules)提供了這種方式,你可以像這樣使用它:

        func QueryRolesByHeaders(header http.Header) (roles []string,err error){
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 這個(gè)邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
            return roles, err
        }
        
        func Authentication() ace.HandlerFunc {
            var advancedRules = loader.AdvancedRules{
                {
                    Host: []string{"*"},
                    Path: []string{"**"},
                    Method: []string{"*"},
                    Permission: &grbac.Permission{
                        AuthorizedRoles: []string{},
                        ForbiddenRoles: []string{"black_user"},
                        AllowAnyone: false,
                    },
                },
                {
                    Host: []string{"domain.com"},
                    Path: []string{"/article"},
                    Method: []string{"PUT","DELETE","POST"},
                    Permission: &grbac.Permission{
                        AuthorizedRoles: []string{"editor"},
                        ForbiddenRoles: []string{},
                        AllowAnyone: false,
                    },
                },
            }
            auth, err := grbac.New(grbac.WithAdvancedRules(advancedRules))
            if err != nil {
                panic(err)
            }
            return func(c *ace.C) {
                roles, err := QueryRolesByHeaders(c.Request.Header)
                if err != nil {
                c.AbortWithStatus(http.StatusInternalServerError)
                    return
                }
                state, err := auth.IsRequestGranted(c.Request, roles)
                if err != nil {
                    c.AbortWithStatus(http.StatusInternalServerError)
                    return
                }
                if !state.IsGranted() {
                    c.AbortWithStatus(http.StatusUnauthorized)
                    return
                }
            }
        }
        
        func main(){
            c := ace.New()
            c.Use(Authentication())
        
            // 在這里通過c.Get、c.Post等函數(shù)綁定你的API
            // ...
            
        }
        

        loader.AdvancedRules試圖提供一種比grbac.Rules更緊湊的定義鑒權(quán)規(guī)則的方法。

        3.5. gin && grbac.WithLoader

        func QueryRolesByHeaders(header http.Header) (roles []string,err error){
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 這個(gè)邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
            return roles, err
        }
        
        type MySQLLoader struct {
            session *gorm.DB
        }
        
        func NewMySQLLoader(dsn string) (*MySQLLoader, error) {
            loader := &MySQLLoader{}
            db, err := gorm.Open("mysql", dsn)
            if err  != nil {
                return nil, err
            }
            loader.session = db
            return loader, nil
        }
        
        func (loader *MySQLLoader) LoadRules() (rules grbac.Rules, err error) {
            // 在這里實(shí)現(xiàn)你的邏輯
            // ...
            // 你可以從數(shù)據(jù)庫或文件加載授權(quán)規(guī)則
            // 但是你需要以 grbac.Rules 的格式返回你的身份驗(yàn)證規(guī)則
            // 提示:你還可以將此函數(shù)綁定到golang結(jié)構(gòu)體
            return
        }
        
        func Authentication() gin.HandlerFunc {
            loader, err := NewMySQLLoader("user:password@/dbname?charset=utf8&parseTime=True&loc=Local")
            if err != nil {
                panic(err)
            }
            rbac, err := grbac.New(grbac.WithLoader(loader.LoadRules, time.Second * 5))
            if err != nil {
                panic(err)
            }
            return func(c *gin.Context) {
                roles, err := QueryRolesByHeaders(c.Request.Header)
                if err != nil {
                    c.AbortWithStatus(http.StatusInternalServerError)
                    return
                }
                    
                state, err := rbac.IsRequestGranted(c.Request, roles)
                if err != nil {
                    c.AbortWithStatus(http.StatusInternalServerError)
                    return
                }
                if !state.IsGranted() {
                    c.AbortWithStatus(http.StatusUnauthorized)
                    return
                }
            }
        }
        
        func main(){
            c := gin.New()
            c.Use(Authorization())
        
            // 在這里通過c.Get、c.Post等函數(shù)綁定你的API
            // ...
        
            c.Run(":8080")
        }

        4. 增強(qiáng)的通配符

        Wildcard支持的語法:

        pattern:
          { term }
        term:
          '*'         匹配任何非路徑分隔符的字符串
          '**'        匹配任何字符串,包括路徑分隔符.
          '?'         匹配任何單個(gè)非路徑分隔符
          '[' [ '^' ] { character-range } ']'
                character class (must be non-empty)
          '{' { term } [ ',' { term } ... ] '}'
          c           匹配字符 c (c != '*', '?', '\\', '[')
          '\\' c      匹配字符 c
        
        character-range:
          c           匹配字符 c (c != '\\', '-', ']')
          '\\' c      匹配字符 c
          lo '-' hi   匹配字符 c for lo <= c <= hi
        

        5. 運(yùn)行效率

        ? gos test -bench=. 
        goos: linux
        goarch: amd64
        pkg: github.com/storyicon/grbac/pkg/tree
        BenchmarkTree_Query                   2000           541397 ns/op
        BenchmarkTree_Foreach_Query           2000           1360719 ns/op
        PASS
        ok      github.com/storyicon/grbac/pkg/tree     13.182s

        測試用例包含1000個(gè)隨機(jī)規(guī)則,“BenchmarkTree_Query”和“BenchmarkTree_Foreach_Query”函數(shù)分別測試四個(gè)請求:

        541397/(4*1e9)=0.0001s
        

        當(dāng)有1000條規(guī)則時(shí),每個(gè)請求的平均驗(yàn)證時(shí)間為“0.0001s”,這很快(大多數(shù)時(shí)間在通配符的匹配上)。

        瀏覽 10
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产精品久久欠久久久久久九秃大 | 欧美乱大交xxxxx | 精品久久三级 | 摘花xxxx过程xxxx | 精品国产乱码久久久久久免费舒淇 | 婷婷综合熟女 | 日韩特黄免费视频 | 日本无遮挡吸乳呻吟视频软件 | 99re这里只有精品8 | 国产精品久久久久久久久久不蜜月 |