最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何在Go中使用Casbin進(jìn)行訪問控制

 更新時(shí)間:2022年08月26日 08:45:27   作者:?jiǎn)炭???????  
這篇文章主要介紹了如何在Go中使用Casbin進(jìn)行訪問控制,Casbin是一個(gè)強(qiáng)大的、高效的開源訪問控制框架,其權(quán)限管理機(jī)制支持多種訪問控制模型,Casbin只負(fù)責(zé)訪問控制

Casbin是什么

Casbin是一個(gè)強(qiáng)大的、高效的開源訪問控制框架,其權(quán)限管理機(jī)制支持多種訪問控制模型,Casbin只負(fù)責(zé)訪問控制。

其功能有:

  • 支持自定義請(qǐng)求的格式,默認(rèn)的請(qǐng)求格式為{subject, object, action}。
  • 具有訪問控制模型model和策略policy兩個(gè)核心概念。
  • 支持RBAC中的多層角色繼承,不止主體可以有角色,資源也可以具有角色。
  • 支持內(nèi)置的超級(jí)用戶 例如:rootadministrator。超級(jí)用戶可以執(zhí)行任何操作而無(wú)需顯式的權(quán)限聲明。
  • 支持多種內(nèi)置的操作符,如 keyMatch,方便對(duì)路徑式的資源進(jìn)行管理,如 /foo/bar 可以映射到 /foo*

Casbin的工作原理

在 Casbin 中, 訪問控制模型被抽象為基于 **PERM **(Policy, Effect, Request, Matcher) [策略,效果,請(qǐng)求,匹配器]的一個(gè)文件。

  • Policy:定義權(quán)限的規(guī)則
  • Effect:定義組合了多個(gè)Policy之后的結(jié)果
  • Request:訪問請(qǐng)求
  • Matcher:判斷Request是否滿足Policy

首先會(huì)定義一堆Policy,讓后通過(guò)Matcher來(lái)判斷Request和Policy是否匹配,然后通過(guò)Effect來(lái)判斷匹配結(jié)果是Allow還是Deny。

Casbin的核心概念

Model

Model是Casbin的具體訪問模型,其主要以文件的形式出現(xiàn),該文件常常以.conf最為后綴。

  • Model CONF 至少應(yīng)包含四個(gè)部分: [request_definition][policy_definition][policy_effect][matchers]。
  • 如果 model 使用 RBAC, 還需要添加[role_definition]部分。
  • Model CONF 文件可以包含注釋。注釋以 # 開頭, # 會(huì)注釋該行剩余部分。

比如:

# Request定義
[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
  • request_definition:用于request的定義,它明確了e.Enforce(...)函數(shù)中參數(shù)的定義,sub, obj, act 表示經(jīng)典三元組: 訪問實(shí)體 (Subject),訪問資源 (Object) 和訪問方法 (Action)。
  • policy_definition:用于policy的定義,每條規(guī)則通常以形如ppolicy type開頭,比如p,joker,data1,read就是一條joker具有data1讀權(quán)限的規(guī)則。
  • role_definition:是RBAC角色繼承關(guān)系的定義。g 是一個(gè) RBAC系統(tǒng),_, _表示角色繼承關(guān)系的前項(xiàng)和后項(xiàng),即前項(xiàng)繼承后項(xiàng)角色的權(quán)限。
  • policy_effect:是對(duì)policy生效范圍的定義,它對(duì)request的決策結(jié)果進(jìn)行統(tǒng)一的決策,比如e = some(where (p.eft == allow))就表示如果存在任意一個(gè)決策結(jié)果為allow的匹配規(guī)則,則最終決策結(jié)果為allow。p.eft 表示策略規(guī)則的決策結(jié)果,可以為allow 或者deny,當(dāng)不指定規(guī)則的決策結(jié)果時(shí),取默認(rèn)值allow 。
  • matchers:定義了策略匹配者。匹配者是一組表達(dá)式,它定義了如何根據(jù)請(qǐng)求來(lái)匹配策略規(guī)則

Policy

Policy主要表示訪問控制關(guān)于角色、資源、行為的具體映射關(guān)系。

比如:

p, alice, data1, read
p, bob, data2, write
p, data2_admin, data2, read
p, data2_admin, data2, write
g, alice, data2_admin

它的關(guān)系規(guī)則很簡(jiǎn)單,主要是選擇什么方式來(lái)存儲(chǔ)規(guī)則,目前官方提供csv文件存儲(chǔ)和通過(guò)adapter適配器從其他存儲(chǔ)系統(tǒng)中加載配置文件,比如MySQL, PostgreSQL, SQL Server, SQLite3,MongoDB,Redis,Cassandra DB等。

實(shí)踐

創(chuàng)建項(xiàng)目

首先創(chuàng)建一個(gè)項(xiàng)目,叫casbin_test。

項(xiàng)目里的目錄結(jié)構(gòu)如下:

├─configs         # 配置文件
├─global				  # 全局變量
├─internal        # 內(nèi)部模塊
│  ├─dao					# 數(shù)據(jù)處理模塊
│  ├─middleware   # 中間件
│  ├─model        # 模型層
│  ├─router       # 路由
│  │  └─api
│  │      └─v1    # 視圖
│  └─service      # 業(yè)務(wù)邏輯層
└─pkg             # 內(nèi)部模塊包
    ├─app         # 應(yīng)用包
    ├─errcode     # 錯(cuò)誤代碼包
    └─setting     # 配置包

下載依賴包,如下:

go get -u github.com/gin-gonic/gin
# Go語(yǔ)言casbin的依賴包
go get github.com/casbin/casbin
# gorm 適配器依賴包
go get github.com/casbin/gorm-adapter
# mysql驅(qū)動(dòng)依賴
go get github.com/go-sql-driver/mysql
# gorm 包
go get github.com/jinzhu/gorm

創(chuàng)建數(shù)據(jù)庫(kù),如下:

CREATE DATABASE `casbin_test` DEFAULT CHARACTER SET utf8;
GRANT Alter, Alter Routine, Create, Create Routine, Create Temporary Tables, Create View, Delete, Drop, Event, Execute, Index, Insert, Lock Tables, References, Select, Show View, Trigger, Update ON `casbin\_test`.* TO `ops`@`%`;
FLUSH PRIVILEGES;
DROP TABLE IF EXIST `casbin_rule`;
CREATE TABLE `casbin_rule` (
  `p_type` varchar(100) DEFAULT NULL COMMENT '規(guī)則類型',
  `v0` varchar(100) DEFAULT NULL COMMENT '角色I(xiàn)D',
  `v1` varchar(100) DEFAULT NULL COMMENT 'api路徑',
  `v2` varchar(100) DEFAULT NULL COMMENT 'api訪問方法',
  `v3` varchar(100) DEFAULT NULL,
  `v4` varchar(100) DEFAULT NULL,
  `v5` varchar(100) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='權(quán)限規(guī)則表';
/*插入操作casbin api的權(quán)限規(guī)則*/
INSERT INTO `casbin_rule`(`p_type`, `v0`, `v1`, `v2`) VALUES ('p', 'admin', '/api/v1/casbin', 'POST');
INSERT INTO `casbin_rule`(`p_type`, `v0`, `v1`, `v2`) VALUES ('p', 'admin', '/api/v1/casbin/list', 'GET');

代碼開發(fā)

由于代碼比較多,這里就不貼全部代碼了,全部代碼已經(jīng)放在gitee倉(cāng)庫(kù),可以自行閱讀,這些僅僅貼部分關(guān)鍵代碼。

(1)首先在configs目錄下創(chuàng)建rbac_model.conf文件,寫入如下代碼:

[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 = r.sub == p.sub && ParamsMatch(r.obj,p.obj) && r.act == p.act

(2)在internal/model目錄下,創(chuàng)建casbin.go文件,寫入如下代碼:

type CasbinModel struct {
	PType  string `json:"p_type" gorm:"column:p_type" description:"策略類型"`
	RoleId string `json:"role_id" gorm:"column:v0" description:"角色I(xiàn)D"`
	Path   string `json:"path" gorm:"column:v1" description:"api路徑"`
	Method string `json:"method" gorm:"column:v2" description:"訪問方法"`
}

func (c *CasbinModel) TableName() string {
	return "casbin_rule"
}

func (c *CasbinModel) Create(db *gorm.DB) error {
	e := Casbin()
	if success := e.AddPolicy(c.RoleId,c.Path,c.Method); success == false {
		return errors.New("存在相同的API,添加失敗")
	}
	return nil
}

func (c *CasbinModel) Update(db *gorm.DB, values interface{}) error {
	if err := db.Model(c).Where("v1 = ? AND v2 = ?", c.Path, c.Method).Update(values).Error; err != nil {
		return err
	}
	return nil
}

func (c *CasbinModel) List(db *gorm.DB) [][]string {
	e := Casbin()
	policy := e.GetFilteredPolicy(0, c.RoleId)
	return policy
}

//@function: Casbin
//@description: 持久化到數(shù)據(jù)庫(kù)  引入自定義規(guī)則
//@return: *casbin.Enforcer
func Casbin() *casbin.Enforcer {
	s := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s&parseTime=%t&loc=Local",
		global.DatabaseSetting.Username,
		global.DatabaseSetting.Password,
		global.DatabaseSetting.Host,
		global.DatabaseSetting.DBName,
		global.DatabaseSetting.Charset,
		global.DatabaseSetting.ParseTime,
	)
	db, _ := gorm.Open(global.DatabaseSetting.DBType, s)

	adapter := gormadapter.NewAdapterByDB(db)
	enforcer := casbin.NewEnforcer(global.CasbinSetting.ModelPath, adapter)
	enforcer.AddFunction("ParamsMatch", ParamsMatchFunc)
	_ = enforcer.LoadPolicy()
	return enforcer
}

//@function: ParamsMatch
//@description: 自定義規(guī)則函數(shù)
//@param: fullNameKey1 string, key2 string
//@return: bool
func ParamsMatch(fullNameKey1 string, key2 string) bool {
	key1 := strings.Split(fullNameKey1, "?")[0]
	// 剝離路徑后再使用casbin的keyMatch2
	return util.KeyMatch2(key1, key2)
}

//@function: ParamsMatchFunc
//@description: 自定義規(guī)則函數(shù)
//@param: args ...interface{}
//@return: interface{}, error
func ParamsMatchFunc(args ...interface{}) (interface{}, error) {
	name1 := args[0].(string)
	name2 := args[1].(string)

	return ParamsMatch(name1, name2), nil
}

(3)在internal/dao目錄下創(chuàng)建casbin.go,寫入如下代碼:

func (d *Dao) CasbinCreate(roleId string, path, method string) error {
	cm := model.CasbinModel{
		PType:  "p",
		RoleId: roleId,
		Path:   path,
		Method: method,
	}
	return cm.Create(d.engine)
}

func (d *Dao) CasbinList(roleID string) [][]string {
	cm := model.CasbinModel{RoleId: roleID}
	return cm.List(d.engine)
}

(4)在internal/service目錄下創(chuàng)建service.go,寫入如下代碼:

type CasbinInfo struct {
	Path   string `json:"path" form:"path"`
	Method string `json:"method" form:"method"`
}
type CasbinCreateRequest struct {
	RoleId      string       `json:"role_id" form:"role_id" description:"角色I(xiàn)D"`
	CasbinInfos []CasbinInfo `json:"casbin_infos" description:"權(quán)限模型列表"`
}

type CasbinListResponse struct {
	List []CasbinInfo `json:"list" form:"list"`
}

type CasbinListRequest struct {
	RoleID string `json:"role_id" form:"role_id"`
}

func (s Service) CasbinCreate(param *CasbinCreateRequest) error {
	for _, v := range param.CasbinInfos {
		err := s.dao.CasbinCreate(param.RoleId, v.Path, v.Method)
		if err != nil {
			return err
		}
	}
	return nil
}


func (s Service) CasbinList(param *CasbinListRequest) [][]string {
	return s.dao.CasbinList(param.RoleID)
}

(5)在internal/router/api/v1目錄下創(chuàng)建casbin.go,寫入如下代碼:

type Casbin struct {
}

func NewCasbin() Casbin {
	return Casbin{}
}

// Create godoc
// @Summary 新增權(quán)限
// @Description 新增權(quán)限
// @Tags 權(quán)限管理
// @Produce json
// @Security ApiKeyAuth
// @Param body body service.CasbinCreateRequest true "body"
// @Success 200 {object} string "成功"
// @Failure 400 {object} errcode.Error "請(qǐng)求錯(cuò)誤"
// @Failure 500 {object} errcode.Error "內(nèi)部錯(cuò)誤"
// @Router /api/v1/casbin [post]
func (c Casbin) Create(ctx *gin.Context) {
	param := service.CasbinCreateRequest{}
	response := app.NewResponse(ctx)
	valid, errors := app.BindAndValid(ctx, &param)
	if !valid {
		log.Printf("app.BindAndValid errs: %v", errors)
		errRsp := errcode.InvalidParams.WithDetails(errors.Errors()...)
		response.ToErrorResponse(errRsp)
		return
	}

	// 進(jìn)行插入操作
	svc := service.NewService(ctx)
	err := svc.CasbinCreate(&param)
	if err != nil {
		log.Printf("svc.CasbinCreate err: %v", err)
		response.ToErrorResponse(errcode.ErrorCasbinCreateFail)
	}
	response.ToResponse(gin.H{})
	return
}


// List godoc
// @Summary 獲取權(quán)限列表
// @Produce json
// @Tags 權(quán)限管理
// @Security ApiKeyAuth
// @Param data body service.CasbinListRequest true "角色I(xiàn)D"
// @Success 200 {object} service.CasbinListResponse "成功"
// @Failure 400 {object} errcode.Error "請(qǐng)求錯(cuò)誤"
// @Failure 500 {object} errcode.Error "內(nèi)部錯(cuò)誤"
// @Router /api/v1/casbin/list [post]
func (c Casbin) List(ctx *gin.Context) {
	param := service.CasbinListRequest{}
	response := app.NewResponse(ctx)
	valid, errors := app.BindAndValid(ctx, &param)
	if !valid {
		log.Printf("app.BindAndValid errs: %v", errors)
		errRsp := errcode.InvalidParams.WithDetails(errors.Errors()...)
		response.ToErrorResponse(errRsp)
		return
	}
	// 業(yè)務(wù)邏輯處理
	svc := service.NewService(ctx)
	casbins := svc.CasbinList(&param)
	var respList []service.CasbinInfo
	for _, host := range casbins {
		respList = append(respList, service.CasbinInfo{
			Path:   host[1],
			Method: host[2],
		})
	}
	response.ToResponseList(respList, 0)
	return
}

再在該目錄下創(chuàng)建一個(gè)test.go文件,用于測(cè)試,代碼如下:

type Test struct {
}

func NewTest() Test {
	return Test{}
}

func (t Test) Get(ctx *gin.Context) {
	log.Println("Hello 接收到GET請(qǐng)求..")
	response := app.NewResponse(ctx)
	response.ToResponse("接收GET請(qǐng)求成功")
}

(6)在internal/middleware目錄下創(chuàng)建casbin_handler.go寫入如下代碼:

func CasbinHandler() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		response := app.NewResponse(ctx)
		// 獲取請(qǐng)求的URI
		obj := ctx.Request.URL.RequestURI()
		// 獲取請(qǐng)求方法
		act := ctx.Request.Method
		// 獲取用戶的角色
		sub := "admin"
		e := model.Casbin()
		fmt.Println(obj, act, sub)
		// 判斷策略中是否存在
		success := e.Enforce(sub, obj, act)
		if success {
			log.Println("恭喜您,權(quán)限驗(yàn)證通過(guò)")
			ctx.Next()
		} else {
			log.Printf("e.Enforce err: %s", "很遺憾,權(quán)限驗(yàn)證沒有通過(guò)")
			response.ToErrorResponse(errcode.UnauthorizedAuthFail)
			ctx.Abort()
			return
		}
	}
}

(7)在internal/router目錄下創(chuàng)建router.go,定義路由,代碼如下:

func NewRouter() *gin.Engine {
	r := gin.New()
	r.Use(gin.Logger())
	r.Use(gin.Recovery())
	casbin := v1.NewCasbin()
	test := v1.NewTest()
	apiv1 := r.Group("/api/v1")
	apiv1.Use(middleware.CasbinHandler())
	{
		// 測(cè)試路由
		apiv1.GET("/hello", test.Get)

		// 權(quán)限策略管理
		apiv1.POST("/casbin", casbin.Create)
		apiv1.POST("/casbin/list", casbin.List)
	}
	return r
}

最后就啟動(dòng)項(xiàng)目進(jìn)行測(cè)試。

驗(yàn)證

(1)首先訪問測(cè)試路徑,當(dāng)前情況下沒在權(quán)限表里,如下: 

(2)將測(cè)試路徑添加到權(quán)限列表,如下: 

 (3)然后再次訪問測(cè)試路徑,如下: 

 并且從日志上也可以看到,如下: 

到此這篇關(guān)于如何在Go中使用Casbin進(jìn)行訪問控制的文章就介紹到這了,更多相關(guān)Go Casbin訪問控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go實(shí)現(xiàn)簡(jiǎn)易R(shí)PC框架的方法步驟

    Go實(shí)現(xiàn)簡(jiǎn)易R(shí)PC框架的方法步驟

    本文旨在講述 RPC 框架設(shè)計(jì)中的幾個(gè)核心問題及其解決方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-03-03
  • 深入了解Golang的指針用法

    深入了解Golang的指針用法

    與C語(yǔ)言一樣,Go語(yǔ)言中同樣有指針,通過(guò)指針,我們可以只傳遞變量的內(nèi)存地址,而不是傳遞整個(gè)變量,這在一定程度上可以節(jié)省內(nèi)存的占用。本文將通過(guò)示例詳細(xì)講講Golang的指針用法,需要的可以參考一下
    2022-07-07
  • Go 第三方庫(kù)之類型轉(zhuǎn)換問題

    Go 第三方庫(kù)之類型轉(zhuǎn)換問題

    今天給大家介紹一個(gè)第三方庫(kù),專門處理類型轉(zhuǎn)換的問題。對(duì)Go 第三方庫(kù)之類型轉(zhuǎn)換問題感興趣的朋友跟隨小編一起看看吧
    2021-08-08
  • 淺析Go語(yǔ)言中數(shù)組的這些細(xì)節(jié)

    淺析Go語(yǔ)言中數(shù)組的這些細(xì)節(jié)

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中數(shù)組一些細(xì)節(jié)的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語(yǔ)言有一定的幫助,需要的可以了解一下
    2022-11-11
  • Go 內(nèi)存分配管理

    Go 內(nèi)存分配管理

    這篇文章主要介紹了Go 內(nèi)存分配管理,go 語(yǔ)言實(shí)際內(nèi)存、虛擬內(nèi)存怎么分配,延遲歸還是什么機(jī)制?本文結(jié)合監(jiān)控對(duì)內(nèi)存管理進(jìn)行了觀測(cè),深入學(xué)習(xí)golang對(duì)于內(nèi)存的管理機(jī)制,需要的朋友可以參考一下
    2022-02-02
  • 詳解Golang時(shí)間處理的踩坑及解決

    詳解Golang時(shí)間處理的踩坑及解決

    在各個(gè)語(yǔ)言之中都有時(shí)間類型的處理,這篇文章主要和大家分享一下Golang進(jìn)行時(shí)間處理時(shí)哪里最容易踩坑以及解決方法,需要的可以參考一下
    2023-01-01
  • Golang反射獲取結(jié)構(gòu)體的值和修改值的代碼示例

    Golang反射獲取結(jié)構(gòu)體的值和修改值的代碼示例

    這篇文章主要給大家介紹了golang反射獲取結(jié)構(gòu)體的值和修改值的代碼示例及演示效果,對(duì)我們的學(xué)習(xí)或工作有一定的幫助,感興趣的同學(xué)可以參考閱讀本文
    2023-08-08
  • Golang設(shè)計(jì)模式之工廠方法模式講解和代碼示例

    Golang設(shè)計(jì)模式之工廠方法模式講解和代碼示例

    工廠方法是一種創(chuàng)建型設(shè)計(jì)模式, 解決了在不指定具體類的情況下創(chuàng)建產(chǎn)品對(duì)象的問題,本文將通過(guò)代碼示例詳細(xì)給大家介紹一下Golang工廠方法模式,感興趣的同學(xué)可以參考一下
    2023-06-06
  • Go 語(yǔ)言中的死鎖問題解決

    Go 語(yǔ)言中的死鎖問題解決

    本文主要介紹了Go 語(yǔ)言中的死鎖問題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Beego中ORM操作各類數(shù)據(jù)庫(kù)連接方式詳細(xì)示例

    Beego中ORM操作各類數(shù)據(jù)庫(kù)連接方式詳細(xì)示例

    這篇文章主要為大家介紹了Beego中ORM操作各類數(shù)據(jù)庫(kù)連接方式詳細(xì)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04

最新評(píng)論

汝阳县| 精河县| 晴隆县| 开平市| 黎城县| 万山特区| 靖西县| 札达县| 鹤岗市| 共和县| 宾阳县| 桦南县| 漳浦县| 文山县| 平山县| 恩施市| 衡阳市| 内乡县| 曲周县| 宜黄县| 荥经县| 尉氏县| 察雅县| 绍兴县| 马山县| 本溪市| 清河县| 任丘市| 明水县| 泰州市| 梅河口市| 兴隆县| 福泉市| 枣强县| 临泽县| 民权县| 崇义县| 海兴县| 诸暨市| 伊吾县| 六安市|