<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

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

          Grbac是一個快速,優(yōu)雅和簡潔的RBAC(基于角色的訪問控制)框架。它支持增強的通配符并使用Radix樹匹配HTTP請求。可以在任何現(xiàn)有的數(shù)據(jù)庫和數(shù)據(jù)結(jié)構(gòu)中輕松使用它。

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

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

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

          1. 最常見的用例

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

          package main
          
          import (
              "github.com/gin-gonic/gin"
              "github.com/storyicon/grbac"
              "net/http"
              "time"
          )
          
          func LoadAuthorizationRules() (rules grbac.Rules, err error) {
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 你可以從數(shù)據(jù)庫或文件加載授權(quán)規(guī)則
              // 但是你需要以 grbac.Rules 的格式返回你的身份驗證規(guī)則
              // 提示:你還可以將此函數(shù)綁定到golang結(jié)構(gòu)體
              return
          }
          
          func QueryRolesByHeaders(header http.Header) (roles []string,err error){
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 這個邏輯可能是從請求的Headers中獲取token,并且根據(jù)token從數(shù)據(jù)庫中查詢用戶的相應(yīng)角色。
              return roles, err
          }
          
          func Authorization() gin.HandlerFunc {
              // 在這里,我們通過“grbac.WithLoader”接口使用自定義Loader功能
              // 并指定應(yīng)每分鐘調(diào)用一次LoadAuthorizationRules函數(shù)以獲取最新的身份驗證規(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)先級越高。
              // 當請求被多個規(guī)則同時匹配時,grbac將僅使用具有最高ID值的規(guī)則。
              // 如果有多個規(guī)則同時具有最大的ID,則將隨機使用其中一個規(guī)則。
              ID int `json:"id"`
              *Resource
              *Permission
          }

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

          下面有一個非常簡單的例子:

          #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可以對文章進行增刪改操作。 這樣,除了文章的操作只能由editor訪問之外,任何具有任何角色的人都可以訪問所有其他資源。

          2.2. Resource

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

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

          Resource的每個字段都支持增強的通配符

          2.3. Permission

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

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

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

          2.4. Loader

          Loader用于加載Rule。 grbac預(yù)置了一些加載器,你也可以通過實現(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的重載周期。 當interval <0時,grbac會放棄周期加載Rules配置; 當interval∈[0,1s)時,grbac會自動將interval設(shè)置為5s;

          3. 其他例子

          這里有一些簡單的例子,可以讓你更容易理解grbac的工作原理。 雖然grbac在大多數(shù)http框架中運行良好,但很抱歉我現(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”格式的身份驗證規(guī)則示例。它的結(jié)構(gòu)基于grbac.Rules

          func QueryRolesByHeaders(header http.Header) (roles []string,err error){
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 這個邏輯可能是從請求的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將每隔一個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”格式的認證規(guī)則的示例。它的結(jié)構(gòu)基于grbac.Rules

          func QueryRolesByHeaders(header http.Header) (roles []string,err error){
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 這個邏輯可能是從請求的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

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

          func QueryRolesByHeaders(header http.Header) (roles []string,err error){
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 這個邏輯可能是從請求的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

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

          func QueryRolesByHeaders(header http.Header) (roles []string,err error){
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 這個邏輯可能是從請求的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){
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 這個邏輯可能是從請求的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) {
              // 在這里實現(xiàn)你的邏輯
              // ...
              // 你可以從數(shù)據(jù)庫或文件加載授權(quán)規(guī)則
              // 但是你需要以 grbac.Rules 的格式返回你的身份驗證規(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. 增強的通配符

          Wildcard支持的語法:

          pattern:
            { term }
          term:
            '*'         匹配任何非路徑分隔符的字符串
            '**'        匹配任何字符串,包括路徑分隔符.
            '?'         匹配任何單個非路徑分隔符
            '[' [ '^' ] { 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. 運行效率

          ? 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個隨機規(guī)則,“BenchmarkTree_Query”和“BenchmarkTree_Foreach_Query”函數(shù)分別測試四個請求:

          541397/(4*1e9)=0.0001s
          

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

          瀏覽 10
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          編輯 分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          編輯 分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花视频免费看 | 中国13一14毛片 | 国产高潮视频在线观看 | 青草草在线视频 | 男人资源站 |